[CodeChef April Challenge 2014] FBCHEF

Description

給出一棵n個點的,以1爲根的有根樹,每個點有點權wi
q次操作,有兩種操作:
1 A X:對於所有的點B,定義dis(A,B)爲A到B路徑上的邊數,將B的權值減去X2dis(A,B)\lfloor {X\over {2^{dis(A,B)}}}\rfloor
2 A:問以A爲根的子樹內,有多少個點的點權<=0
n,q,X<=10^5

Solution

先考慮修改在詢問前的做法
設F[x][k]表示以x爲根的子樹內,到x距離爲k的點的修改值
那麼一次修改我們會修改F[A][0…16],F[father[A]][0…15],…
然後我們詢問考慮直接問A的每個祖先B的F[B][dis(A,B)]
注意到這裏可能會算重,於是我們F數組記得實際上是修改值的差分值
注意到點權只會減少,於是我們可以考慮求出所有點第一次變成非正的時間
這個東西可以直接套整體二分
考慮一次修改複雜度爲O(log^2 A),詢問複雜度爲O(log A),於是我們可以預先把操作加入,然後可持久化一下,這樣整體二分的時候就不用重新加入修改
因爲我們不能在可持久化的數組上二分,我們可以直接維護一個指針掃一掃
整體二分的時候時間的流動是有序的,指針掃動的總次數爲O(n log^2 A)
那麼總複雜度就是O(n log^2 A)

Code

#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a) for(int i=lst[a];i;i=nxt[i])
#define pb(a) push_back(a)
#define mp(a,b) make_pair(a,b)
using namespace std;

typedef long long ll;

int read() {
	char ch;
	for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
	int x=ch-'0';
	for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x;
}

void write(int x) {
	if (!x) {puts("0");return;}
	char ch[20];int tot=0;
	for(;x;x/=10) ch[++tot]=x%10+'0';
	fd(i,tot,1) putchar(ch[i]);
	puts("");
}

const int N=1e5+5,D=17;

int t[N<<1],nxt[N<<1],lst[N],l;
void add(int x,int y) {t[++l]=y;nxt[l]=lst[x];lst[x]=l;}

int n,q,w[N],dfn[N],tot,tm,qu,fir[N],sz[N],fa[N],dep[N];

struct Ask{int x,t;}ask[N];
struct Atk{int x,v;}atk[N];

int tr[N];
void I(int x) {for(;x<=n;x+=x&-x) tr[x]++;}
int Q(int x) {int ret=0;for(;x;x-=x&-x) ret+=tr[x];return ret;}

int ord[N];
bool cmp(int x,int y) {return fir[x]<fir[y];}

void dfs(int x,int y) {
	fa[x]=y;dfn[x]=++tot;sz[x]=1;dep[x]=dep[y]+1;
	rep(i,x) if (t[i]!=y) dfs(t[i],x),sz[x]+=sz[t[i]];
}

int id[N],pt[N][D],p1[N],p2[N],cnt;
vector<ll> f[N][D];
vector<int> tim[N][D];

void solve(int l,int r,int x,int y) {
	if (l==r) {fo(i,x,y) fir[id[i]]=l;return;}
	int mid=l+r>>1,t1=0,t2=0;
	fo(i,x,y) {
		ll c=0;
		for(int z=id[i],j=0;j<=16&&z;z=fa[z],j++) {
			if (tim[z][j].empty()) continue;
			int pos=pt[z][j];
			while (pos<tim[z][j].size()-1&&tim[z][j][pos+1]<=mid) pos++,cnt++;
			while (pos>=0&&tim[z][j][pos]>mid) pos--,cnt++;
			if (pos>=0) c+=f[z][j][pos];
			pt[z][j]=max(pos,0);
		}
		if (c>=w[id[i]]) p1[++t1]=id[i];
		else p2[++t2]=id[i];
	}
	fo(i,1,t1) id[x+i-1]=p1[i];
	fo(i,1,t2) id[x+t1+i-1]=p2[i];
	solve(l,mid,x,x+t1-1);solve(mid+1,r,x+t1,y);
}

int main() {
	freopen("pang.in","r",stdin);
	freopen("pang.out","w",stdout);
	n=read();
	fo(i,1,n) w[i]=read();
	fo(i,1,n-1) {
		int x=read(),y=read();
		add(x,y);add(y,x);
	}
	q=read();dfs(1,0);
	tm=qu=0;
	fo(i,1,q) {
		int opt=read(),x=read();
		if (opt==1) atk[++tm].x=x,atk[tm].v=read();
		if (opt==2) ask[++qu].x=x,ask[qu].t=tm;
	}
	fo(i,1,tm) {
		int x=atk[i].x,v=atk[i].v;
		for(int j=0;j<=16&&x;j++,x=fa[x]) {
			fo(k,0,16-j) {
				ll tmp=(v>>j+k);
				if (fa[x]) tmp-=v>>j+k+2;
				if (!f[x][k].empty()) tmp+=f[x][k][f[x][k].size()-1];
				f[x][k].pb(tmp);tim[x][k].pb(i);
			}
		}
	}
	fo(i,1,n) id[i]=i;solve(1,tm+1,1,n);
	fo(i,1,n) ord[i]=i;sort(ord+1,ord+n+1,cmp);
	int j=0;
	fo(i,1,qu) {
		while (j<n&&fir[ord[j+1]]<=ask[i].t) I(dfn[ord[++j]]);
		write(Q(dfn[ask[i].x]+sz[ask[i].x]-1)-Q(dfn[ask[i].x]-1));
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章