[bzoj4129][樹上帶修莫隊][分塊]Haruna’s Breakfast
Description
Haruna每天都會給提督做早餐! 這天她發現早飯的食材被調皮的 Shimakaze放到了一棵
樹上,每個結點都有一樣食材,Shimakaze要考驗一下她。 每個食材都有一個美味度,Shimakaze會進行兩種操作: 1、修改某個結點的食材的美味度。 2、對於某條鏈,詢問這條鏈的美味度集合中,最小的未出現的自然數是多少。即mex值。 請你幫幫Haruna吧。
Input
第一行包括兩個整數n,m,代表樹上的結點數(標號為1~n)和運算元。
第二行包括n個整數a1…an,代表每個結點的食材初始的美味度。 接下來n-1行,每行包括兩個整數u,v,代表樹上的一條邊。 接下來m 行,每行包括三個整數 0 u x 代表將結點u的食材的美味度修改為 x。 1 u v 代表詢問以u,v 為端點的鏈的mex值。
Output
對於每次詢問,輸出該鏈的mex值。
Sample Input
10 10
1 0 1 0 2 4 4 0 1 0
1 2
2 3
2 4
2 5
1 6
6 7
2 8
3 9
9 10
0 7 14
1 6 6
0 4 9
1 2 2
1 1 8
1 8 3
0 10 9
1 3 5
0 10 0
0 7 7
Sample Output
0
1
2
2
3
HINT
1<=n<=5*10^4
1<=m<=5*10^4
0<=ai<=10^9
題解
學了一發樹上莫隊
按括號序搞的好資瓷啊我就不樹上分塊了吧來一發括號序 其實就是每個點在訪問完他的子樹後再向DFS序列中加入他 兩個點的路徑就可以用他們之間的括號序表示出來 注意括號序在前的點要用他出dfs的編號 在後的要用進dfs的編號 如此 不在路徑上的點要不被算了兩次要不一次都沒有被計算 莫隊的時候判掉即可 注意LCA是否在這兩個點的情況 如果LCA不在這兩個點 那在莫隊完之後 你還要暴力把LCA的貢獻加入再計算答案 因為LCA不在括號序中 這題再對權值分一下塊… 就沒了真的難碼啊
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include<ctime> #define LL long long #define mp(x,y) make_pair(x,y) using namespace std; inline int read() { int f=1,x=0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void write(int x) { if(x<0)putchar('-'),x=-x; if(x>9)write(x/10); putchar(x%10+'0'); } inline void print(int x){write(x);printf(" ");} struct node{int x,y,next;}a[210000];int len,last[110000]; void ins(int x,int y){len++;a[len].x=x;a[len].y=y;a[len].next=last[x];last[x]=len;} int pa[110000],in[110000],ot[110000],dfn; int fa[110000][25],bin[25],dep[110000]; void pre_tree_node(int x) { in[x]=++dfn;pa[dfn]=x; for(int i=1;bin[i]<=dep[x];i++)fa[x][i]=fa[fa[x][i-1]][i-1]; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y!=fa[x][0]) { fa[y][0]=x;dep[y]=dep[x]+1; pre_tree_node(y); } } ot[x]=++dfn;pa[dfn]=x; } int lca(int x,int y) { if(dep[x]<dep[y])swap(x,y); for(int i=20;i>=0;i--)if(bin[i]<=dep[x]&&dep[fa[x][i]]>=dep[y])x=fa[x][i]; if(x==y)return x; for(int i=20;i>=0;i--)if(bin[i]<=dep[x]&&fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i]; return fa[x][0]; } struct modify{int u,c,pre;}B[210000];int l2; struct ask{int l,r,T,op;}A[110000];int l1; int pos[110000],block; bool cmp(ask n1,ask n2) { if(pos[n1.l]!=pos[n2.l])return pos[n1.l]<pos[n2.l]; if(pos[n1.r]!=pos[n2.r])return pos[n1.r]<pos[n2.r]; return n1.T<n2.T; } int n,m; int hh[110000]; int l,r,vis[110000]; int block1,numb,p1[110000],st[320],ed[320]; int s1[320],s2[320][50005];//這個塊裡有多少數字 這個塊裡的這些數字出現了多少次 int col[110000];//這個點當前的數字是什麼 void cal(int x,int op) { int u,p; if(op==1)u=pa[x],p=p1[col[u]];//序列中這個點實際樹上的位置 塊的位置 else u=x,p=p1[col[u]]; if(vis[u]){if(!(--s2[p][col[u]]))s1[p]--;} else { if(!s2[p][col[u]])s1[p]++; s2[p][col[u]]++; }vis[u]^=1; } void ch(int u,int c)//把樹上點u改成c { int p=p1[col[u]]; if(vis[u]){if(!(--s2[p][col[u]]))s1[p]--;} col[u]=c;p=p1[col[u]]; if(vis[u]) { if(!s2[p][col[u]])s1[p]++; s2[p][col[u]]++; } } void up_time(int t1,int t2) { for(int i=t1;i>=t2+1;i--)ch(B[i].u,B[B[i].pre].c); for(int i=t1+1;i<=t2;i++)ch(B[i].u,B[i].c); } int getmex() { int i; for(i=1;i<=numb;i++)if(s1[i]!=block1)break; for(int j=(i==1?0:block1*(i-1));j<=block1*i;j++)if(s2[i][j]==0)return j; } int ans[110000]; int main() { bin[0]=1;for(int i=1;i<=20;i++)bin[i]=bin[i-1]<<1; n=read();m=read();memset(hh,-1,sizeof(hh)); block1=int(sqrt(double(n))); for(int i=0;i<=n;i++) { p1[i]=i/block1+1; if(i!=0&&p1[i]!=p1[i-1])st[p1[i]]=i,ed[p1[i-1]]=i-1; } numb=p1[n];st[1]=0;ed[block1]=n; for(int i=1;i<=n;i++) { B[i].c=read(),B[i].u=i; if(B[i].c>n)B[i].c=n; B[i].pre=hh[B[i].u]; hh[B[i].u]=i; } for(int i=1;i<n;i++) { int x=read(),y=read(); ins(x,y);ins(y,x); } pre_tree_node(1);l2=n; for(int i=1;i<=m;i++) { int op=read(),x=read(),y=read(); if(!op) { l2++;B[l2].c=y;B[l2].u=x; if(B[l2].c>n)B[l2].c=n; B[l2].pre=hh[B[l2].u]; hh[B[l2].u]=l2; } else { int LA=lca(x,y); if(in[x]>in[y])swap(x,y); if(LA!=x&&LA!=y)A[++l1].l=ot[x],A[l1].r=in[y],A[l1].T=l2,A[l1].op=l1; else A[++l1].l=in[x],A[l1].r=in[y],A[l1].T=l2,A[l1].op=l1; } } // for(int i=1;i<=l2;i++)printf("%d %d %d %d\n",i,B[i].u,B[i].c,B[i].pre); //init block=pow(n,2.0/3.0); for(int i=1;i<=2*n;i++)pos[i]=(i-1)/block+1; sort(A+1,A+1+l1,cmp); int T=A[1].T; up_time(0,A[1].T); for(int i=A[1].l;i<=A[1].r;i++) cal(i,1); int LA=lca(pa[A[1].l],pa[A[1].r]); if(LA!=pa[A[1].l]&&LA!=pa[A[1].r]) { cal(LA,2); ans[A[1].op]=getmex(); cal(LA,2); } else ans[A[1].op]=getmex(); l=A[1].l;r=A[1].r; for(int i=2;i<=m;i++) { up_time(T,A[i].T);T=A[i].T; while(l<A[i].l)cal(l++,1); while(l>A[i].l)cal(--l,1); while(r<A[i].r)cal(++r,1); while(r>A[i].r)cal(r--,1); LA=lca(pa[A[i].l],pa[A[i].r]); if(LA!=pa[A[i].l]&&LA!=pa[A[i].r]) { cal(LA,2); ans[A[i].op]=getmex(); cal(LA,2); } else ans[A[i].op]=getmex(); } for(int i=1;i<=l1;i++)printf("%d\n",ans[i]); return 0; }