1. 程式人生 > >BZOJ 3720 [洛谷P2137] : Gty的妹子樹

BZOJ 3720 [洛谷P2137] : Gty的妹子樹

Description

我曾在絃歌之中聽過你,

檀板聲碎,半出摺子戲。

舞榭歌臺被風吹去,

歲月深處尚有餘音一縷……

Gty神(xian)犇(chong)從來不缺妹子……

他來到了一棵妹子樹下,發現每個妹子有一個美麗度……

由於Gty很哲♂學,他只對美麗度大於某個值的妹子感興趣。

他想知道某個子樹中美麗度大於k的妹子個數。

某個妹子的美麗度可能發生變化……

樹上可能會出現一隻新的妹子……

維護一棵初始有n個節點的有根樹(根節點為1),樹上節點編號為1-n,每個點有一個權值wi。

支援以下操作:

0 u x 詢問以u為根的子樹中,嚴格大於x的值的個數。(u^=lastans,x^=lastans)

1 u x 把u節點的權值改成x。(u^=lastans,x^=lastans)

2 u x 新增一個編號為"當前樹中節點數+1"的節點,其父節點為u,其權值為x。(u^=lastans,x^=lastans)

最開始時lastans=0。

Input

輸入第一行包括一個正整數n(1<=n<=30000),代表樹上的初始節點數。

接下來n-1行,每行2個整數u,v,為樹上的一條無向邊。

任何時刻,樹上的任何權值大於等於0,且兩兩不同。

接下來1行,包括n個整數wi,表示初始時每個節點的權值。

接下來1行,包括1個整數m(1<=m<=30000),表示操作總數。

接下來m行,每行包括三個整數 op,u,v:

op,u,v的含義見題目描述。

保證題目涉及的所有數在int內。

Output

對每個op=0,輸出一行,包括一個整數,意義見題目描述。

Sample Input

2

1 2

10 20

1

0 1 5

Sample Output

2

Solution

  • 大家多用的是樹分塊的方法,這裡我用的歸併樹+定期重構。

  • 具體怎樣呢?關鍵是我們考慮每個修改對之後詢問的影響。

  • 如果沒有修改(靜態詢問),我們對dfs序建歸併樹,直接區間查詢即可。

  • (歸併樹就是一種線段樹,區間記憶體的是這個區間權值排序後的序列,查詢時在上面二分)

  • 有了修改,我們就要判斷修改對詢問有影響,其中修改點要在詢問點的子樹內。

  • 如何判斷是否在子樹內:倍增跳。加點時處理出其 2i2^i 級父親。

  • 於是我們得到這樣一個演算法:我們在歸併樹中查詢後,針對若干修改操作暴力判斷影響。

  • 那我們就可以定期重構啦!

  • 如果我們在查詢之前的修改不超過 m\sqrt m 次時,就在歸併樹上查詢後暴力掃描修改計算貢獻;

  • 如果修改超過了 m\sqrt m 次時,我們只要根據修改重建一下歸併樹就可以清除掉這些修改,

  • 可以發現歸併樹的重建不會超過 m\sqrt m 次。

  • 那麼我們來分析一下複雜度:(假設 n,mn,m 同階)

  • 每次掃描修改算貢獻,修改最多 n\sqrt n 個,每次倍增判是否在子樹要 O(logn)O(log\ n) ,複雜度為 O(nnlogn)O(n\sqrt n\ log\ n)

  • 每次重建歸併樹要 O(nlogn)O(n\ log\ n) ,最多重建 O(logn)O(log\ n) 次,故複雜度同是 O(nnlogn)O(n\sqrt n\ log\ n)

  • 於是這題就解決了,總時間複雜度 O(nnlogn)O(n\sqrt n\ log\ n)

  • 有一些細節要注意:

  • 由於重建歸併樹常數比較大,我們可以多幾次修改再重建一次,比如說 5n5*\sqrt n

  • 還有就是打線段樹詢問時:find(1,1,n)find(1,1,n) ,由於加點時 nn 會增加,但帶進詢問時仍然要是之前的 nn ,不然就不對了,重構時再把 find(1,1,n)find(1,1,n)nn 改成新的。

Code

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<cctype>
using namespace std;
const int N=60005;
struct data
{
	int op,x,y,z;
}q[N>>1];
int n,tot,num,cnt,qx,qy,qz,last,lim;
int first[N],nex[N<<1],en[N<<1];
int w[N],dfn[N],size[N],id[N],dep[N],fa[N][16];
vector<int>ss[N<<2];
inline int read()
{
	int X=0,w=0; char ch=0;
	while(!isdigit(ch)) w|=ch=='-',ch=getchar();
	while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
	return w?-X:X;
}
void write(int x)
{
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
inline void insert(int x,int y)
{
	nex[++tot]=first[x];
	first[x]=tot;
	en[tot]=y;
}
void dfs(int x)
{
	dfn[x]=++cnt;
	id[cnt]=x;
	size[x]=1;
	dep[x]=dep[fa[x][0]]+1;
	for(int i=first[x];i;i=nex[i])
		if(en[i]^fa[x][0])
		{
			fa[en[i]][0]=x;
			dfs(en[i]);
			size[x]+=size[en[i]];
		}
}
void make(int v,int l,int r)
{
	ss[v].clear();
	if(l==r)
	{
		ss[v].push_back(w[id[l]]);
		return;
	}
	int mid=l+r>>1,ls=v<<1,rs=ls|1;
	make(ls,l,mid);
	make(rs,mid+1,r);
	int i=0,ni=ss[ls].size()-1;
	int j=0,nj=ss[rs].size()-1;
	while(i<=ni && j<=nj) ss[v].push_back(ss[ls][i]<ss[rs][j]?ss[ls][i++]:ss[rs][j++]);
	while(i<=ni) ss[v].push_back(ss[ls][i++]);
	while(j<=nj) ss[v].push_back(ss[rs][j++]);
}
int find(int v,int l,int r)
{
	if(qx<=l && r<=qy) return ss[v].size()-(upper_bound(ss[v].begin(),ss[v].end()--,qz)-ss[v].begin());
	int mid=l+r>>1,s=0;
	if(qx<=mid) s=find(v<<1,l,mid);
	if(qy>mid) s+=find(v<<1|1,mid+1,r);
	return s;
}
void dfs1(int x)
{
	size[x]=1;
	dfn[x]=++cnt;
	id[cnt]=x;
	for(int i=first[x];i;i=nex[i])
		if(en[i]^fa[x][0])
		{
			dfs(en[i]);
			size[x]+=size[en[i]];
		}
}
inline void rebuild()
{
	cnt=num=0;
	dfs1(1);
	make(1,1,n);
	cnt=n;
}
inline bool belong(int x,int y)
{
	if(dep[x]<dep[y]) return false;
	for(int i=log2(dep[x]);i>=0;i--)
		if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
	return x==y;
}
int main()
{
	n=read();
	for(int i=1;i<n;i++)
	{
		int x=read(),y=read();
		insert(x,y);
		insert(y,x);
	}
	for(int i=1;i<=n;i++) w[i]=read();
	dfs(1);
	cnt=n;
	for(int j=1;j<16;j++)
		for(int i=1;i<=n;i++)
			fa[i][j]=fa[fa[i][j-1]][j-1];
	make(1,1,n);
	int m=read();
	lim=ceil(sqrt(m)*5);
	while(m--)
	{
		int op=read(),x=read()^last,y=read()^last;
		if(op==0)
		{
			if(x<=cnt)
			{
				qx=dfn[x],qy=dfn[x]+size[x]-1,qz=y;
				last=find(1,1,cnt);
			}else last=0;
			for(int i=1;i<=num;i++)
				if(q[i].op==1)
				{
					if((q[i].y<y)^(q[i].z<y) && belong(q[i].x,x)) last+=q[i].z>y?1:-1;
				}else
				{
					if(q[i].z>y && belong(q[i].x,x)) last++;
				}
			write(last),putchar('\n');
		}else
		if(op==1)
		{
			q[++num]=(data){1,x,w[x],y};
			w[x]=y;
			if(num==lim) rebuild();
		}else
		{
			q[++num]=(data){2,++n,x,y};
			insert(x,n);
			insert(n,x);
			fa[n][0]=x;
			dep[n]=dep[x]+1;
			w[n]=y;
			for(int i=1;i<16;i++) fa[n][i]=fa[fa[n][i-1]][i-1];
			if(num==lim) rebuild();
		}
	}
	return 0;
}