1760:树上数颜色
1760:树上数颜色
时间限制: 3000 ms 内存限制: 524288 KB
提交数: 35 通过数: 9
【题目描述】
送你一棵n个点的树,树根为1。一开始每个点上有一个1∼n 的颜色ci,不同点颜色可以相同。
现在有 q 次操作, 分为两种类型:
1 u l r:询问子树 u 中有多少种在 l 到 r 之间的颜色至少出现了一次;
2 u c:将 u 的颜色修改为 c。
部分测试点要求强制在线。
【输入】
第一行三个整数n,q,t,分别表示树的点数,操作的个数和是否强制在线。
t=0表示不强制在线,t=1表示强制在线。
接下来一行nn个整数 ci,表示每个点的初始颜色。
接下来n−1行,每行两个整数ui;vi表示一条ui到vi的边。
接下来q行,每行四个或三个整数,表示一个操作。
当t=1时,需要对第一个数以外的其他数异或上一次询问的答案lastans,初始时lastans=0。
【输出】
对于每个询问输出一行一个整数,表示答案。
【输入样例】
5 5 0
5 5 2 5 5
5 1
2 5
4 2
3 5
1 2 2 3
2 5 1
1 1 1 5
2 3 2
1 3 1 5
【输出样例】
0
3
1
【提示】
【输入样例2】
5 5 1
4 1 1 5 4
5 1
3 5
2 3
4 3
2 5 4
2 2 2
1 3 1 5
2 1 2
1 1 2 7
【输出样例2】
3
1
【数据规模和约定】
对于前20%的数据,n,q≤5000n,q≤5000。
对于前40%的数据,n,q≤50000n,q≤50000。
对于另20%的数据,没有修改操作。
对于另20%的数据,t=0t=0。
对于100%的数据,1≤n,q≤1000001≤n,q≤100000。
【题解】
考虑对于每种颜色,在子树中出现过就是他在dfn[u]到low[u]之间,但要求重复的颜色只算一次,可以将所有同一种颜色的节点按照dfn升序排列,树上差分维护,将所有点的权值加一,并将相邻的点的lca权值减一。对每个颜色建一颗线段树来维护区间求和。考虑使用树状数组套线段树优化,询问直接询问即可。实现较为复杂,代码如下:
#include<bits/stdc++.h> using namespace std; const int N=1e5+5; set <int> s[N]; int n,q,t,last[N],size,f[N][20],m,dfn[N],low[N],dep[N],cnt,dui[N],col[N],lastans; struct pigu { int dao,ne; }a[N<<1]; inline void lingjiebiao(int x,int y) { a[++size].dao=y; a[size].ne=last[x]; last[x]=size; } inline int read() { int x=0,f=1; char c=getchar(); while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();} while(isdigit(c)) {x=(x<<3)+(x<<1)+c-'0';c=getchar();} return x*f; } int sum[N<<8],lc[N<<8],rc[N<<8],ge,root[N]; inline int get_lca(int x,int y) { if(dep[x]<dep[y]) swap(x,y); for(int i=19;i>=0;i--) if(dep[f[x][i]]>=dep[y]) x=f[x][i]; if(x==y) return x; for(int i=19;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0]; } inline void dfs(int now,int fa) { dfn[now]=++cnt;dui[cnt]=now; f[now][0]=fa;dep[now]=dep[fa]+1; for(int i=1;f[f[now][i-1]][i-1];i++) f[now][i]=f[f[now][i-1]][i-1]; for(int i=last[now];i;i=a[i].ne) { if(a[i].dao==fa) continue; dfs(a[i].dao,now); } low[now]=cnt; } inline int lowbit(int x) { return x&(-x); } inline void modify(int &now,int l,int r,int zai,int sf) { if(!now) now=++ge; sum[now]+=sf; if(l==r) return; int mid=(l+r)>>1; if(zai<=mid) modify(lc[now],l,mid,zai,sf); else modify(rc[now],mid+1,r,zai,sf); } inline void gainei(int se,int zai,int pan) { set <int>::iterator it; it=s[se].find(zai); if(it!=s[se].begin()) { it--; int ga=dui[*it]; int gu=get_lca(dui[zai],dui[*it]); for(int i=se;i<=n;i+=lowbit(i)) modify(root[i],1,n,dfn[gu],-pan); it++;it++; if(it!=s[se].end()) { gu=get_lca(ga,dui[*it]); for(int i=se;i<=n;i+=lowbit(i)) modify(root[i],1,n,dfn[gu],pan); } it--; } it++; if(it!=s[se].end()) { int gu=get_lca(dui[zai],dui[*it]); for(int i=se;i<=n;i+=lowbit(i)) modify(root[i],1,n,dfn[gu],-pan); it--; } } inline void insert(int se,int zai) { s[se].insert(dfn[zai]);gainei(se,dfn[zai],1); for(int i=se;i<=n;i+=lowbit(i)) modify(root[i],1,n,dfn[zai],1); } inline int query(int now,int l,int r,int L,int R) { if(!now) return 0; if(l>=L&&r<=R) { return sum[now]; } int mid=(l+r)>>1,daan=0; if(L<=mid) daan+=query(lc[now],l,mid,L,R); if(R>=mid+1) daan+=query(rc[now],mid+1,r,L,R); return daan; } inline void shan(int se,int zai) { gainei(se,dfn[zai],-1);s[se].erase(dfn[zai]); for(int i=se;i<=n;i+=lowbit(i)) modify(root[i],1,n,dfn[zai],-1); } int main() { n=read();q=read();t=read(); for(int i=1;i<=n;i++) col[i]=read(); for(int i=1,x,y;i<=n-1;i++) { x=read();y=read(); lingjiebiao(x,y); lingjiebiao(y,x); } dfs(1,0); for(int i=1;i<=n;i++) insert(col[i],i); for(int i=1,x,y,z,u;i<=q;i++) { x=read();y=read();z=read(); if(t==1) y^=lastans,z^=lastans; if(x==2) { shan(col[y],y);col[y]=z; insert(col[y],y); } else { u=read(); if(t==1) u^=lastans; int ans=0; for(int i=u;i;i-=lowbit(i)) ans+=query(root[i],1,n,dfn[y],low[y]); for(int i=z-1;i;i-=lowbit(i)) ans-=query(root[i],1,n,dfn[y],low[y]); lastans=ans; cout<<ans<<"\n"; } } }