Scx117
只一眼,便辽阔了时间。

题意:给你一棵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\n",ans[tot--]);
73     return 0;
74 }

 

题解:并查集+技巧

暴力可以过很多啊,倒着枚举编号点,判断x和该编号点的路径上是否有银行,树链剖分+线段树(lct)维护即可。

因为连通块拆分比较麻烦,考虑倒着执行操作,相当于删去银行。每删去一个银行就相当于把若干个连通块合并。

询问即是问除了x点所在连通块其他部分的第二大。维护一个保存每个连通块最大次大的set,取出不等于当前连通块最大次大的第二大元素,最多取4次即可。

时间复杂度O((n+m)(logn+a(n))。

posted on 2018-04-24 20:03  Scx117  阅读(101)  评论(0编辑  收藏  举报