题意:给你一棵n个点的树。m个操作,op 1:在点i上建立银行。op 2:询问从点x开始可以经过至少一个银行走到的点中编号第二大的点。
n,m<=1e5.
标程:
1 #include<bits/stdc++.h> 2 using namespace std; 3 int read() 4 { 5 int x=0;char ch=getchar(); 6 while (ch<'0'||ch>'9') ch=getchar(); 7 while ('0'<=ch&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); 8 return x; 9 } 10 const int N=100005; 11 set<int,greater<int> > s; 12 set<int,greater<int> >::iterator it; 13 int cnt,head[N],Mx[N],Mxc[N],f[N],tot,ans[N],tag[N],u,v,n,m; 14 struct node{int to,next;}num[N*2]; 15 struct _node{int op,x;}q[N]; 16 void add(int x,int y) 17 {num[++cnt].to=y;num[cnt].next=head[x];head[x]=cnt;} 18 int find(int x){return x==f[x]?x:f[x]=find(f[x]);} 19 void merge(int x,int y) 20 { 21 if (x==y) return; 22 s.erase(Mx[x]);s.erase(Mx[y]); 23 s.erase(Mxc[x]);s.erase(Mxc[y]); 24 if (Mx[x]>Mx[y]) Mxc[y]=Mx[y],Mx[y]=Mx[x];//注意最大次大的传递 25 else if (Mx[x]>Mxc[y]) Mxc[y]=Mx[x]; 26 if (Mxc[x]>Mxc[y]) Mxc[y]=Mxc[x]; 27 s.insert(Mx[y]);s.insert(Mxc[y]); 28 f[x]=y; 29 } 30 void dfs(int x,int fa) 31 { 32 if (tag[x]) s.insert(x);//银行作为单点也要加入 33 for (int i=head[x];i;i=num[i].next) 34 if (num[i].to!=fa) 35 { 36 dfs(num[i].to,x); 37 if (!tag[num[i].to]&&!tag[x]) merge(find(num[i].to),find(x)); 38 } 39 } 40 int qry(int x) 41 { 42 int fl=0;x=find(x); 43 for (it=s.begin();it!=s.end();++it) 44 { 45 if (*it==Mx[x]||*it==Mxc[x]) continue; 46 if (!fl) fl=1;else return *it; 47 } 48 return -1; 49 } 50 int main() 51 { 52 n=read();m=read();tot=0;s.clear(); 53 memset(head,0,sizeof(head));cnt=0; 54 for (int i=1;i<n;i++) u=read(),v=read(),add(v,u),add(u,v); 55 for (int i=1;i<=n;i++) f[i]=i,Mx[i]=i,Mxc[i]=0,s.insert(i); 56 for (int i=1;i<=m;i++) 57 { 58 q[i].op=read(),q[i].x=read(); 59 if (q[i].op==1) tag[q[i].x]++;//有可能被该银行被统计多次 60 } 61 dfs(1,-1); 62 for (int i=m;i>=1;i--) 63 { 64 if (q[i].op==1) 65 { 66 int now=find(q[i].x);tag[q[i].x]--; 67 if (!tag[q[i].x]) 68 for (int j=head[q[i].x];j;j=num[j].next) 69 if (!tag[num[j].to]) merge(find(num[j].to),now); 70 }else ans[++tot]=qry(q[i].x); 71 } 72 while (tot) printf("%d ",ans[tot--]); 73 return 0; 74 }
题解:并查集+技巧
暴力可以过很多啊,倒着枚举编号点,判断x和该编号点的路径上是否有银行,树链剖分+线段树(lct)维护即可。
因为连通块拆分比较麻烦,考虑倒着执行操作,相当于删去银行。每删去一个银行就相当于把若干个连通块合并。
询问即是问除了x点所在连通块其他部分的第二大。维护一个保存每个连通块最大次大的set,取出不等于当前连通块最大次大的第二大元素,最多取4次即可。
时间复杂度O((n+m)(logn+a(n))。