异象石(信息学奥赛一本通 1554)

【题目描述】

原题来自:Contest Hunter Round #56

在 Adera 的异时空中有一张地图。这张地图上有 NN 个点,有 N1N−1 条双向边把它们连通起来。起初地图上没有任何异象石,在接下来的 MM 个时刻中,每个时刻会发生以下三种类型的事件之一:

地图的某个点上出现了异象石(已经出现的不会再次出现);

地图某个点上的异象石被摧毁(不会摧毁没有异象石的点);

向玩家询问使所有异象石所在的点连通的边集的总长度最小是多少。

请你作为玩家回答这些问题。下图是一个例子,灰色节点表示出现了异象石,加粗的边表示被选为连通异象石的边集。

【输入】

第一行有一个整数 NN,表示点的个数;

接下来 N1N−1 行每行三个整数 x,y,zx,y,z,表示点 xx 和 yy 之间有一条长度为 zz 的双向边;

第 N+1N+1 行有一个正整数 MM;

接下来 MM 行每行是一个事件,事件是以下三种格式之一:

+ x+ x:表示点 xx 上出现了异象石;

 x− x:表示点 xx 上的异象石被摧毁;

??:表示询问使当前所有异象石所在的点连通所需的边集的总长度最小是多少。

【输出】

对于每个 ?? 事件,输出一个整数表示答案。

【输入样例】

6 
1 2 1 
1 3 5 
4 1 7 
4 5 3 
6 4 2 
10 
+ 3 
+ 1 
? 
+ 6 
? 
+ 5 
? 
- 6 
- 3 
?

【输出样例】

5 
14 
17 
10

【提示】

数据范围与提示:

对于 30% 的数据,1n,m1031≤ n,m ≤ 103 ;

对于另 20% 的数据,地图是一条链,或者一朵菊花;

对于 100% 的数据,1n,m105,1x,yn,xy,1z1091≤ n,m ≤ 105,1 ≤ x,y ≤ n,x≠ y,1 ≤ z ≤ 109 。


根据题意,这张地图显然构成一棵树。我们可以先对这棵树进行深度优先遍历,求出每个点的时间戳

通过仔细思考  瞎猜找规律不难发现,如果我们按照时间戳从小到大的顺序,把出现异象石的结点排成一圈(首尾相连哟),并且累加相邻两个结点之间的路径长度,最后得到的结果恰好就是所求结果的两倍。(不太明白的话可以在纸上画一画哟)然鹅我也母鸡该如何证明...要是有小可爱能帮忙证下就好惹

因此,我们可以用一个数据结构(这里我选择的是比较方便好用的C++ STL Set),按照时间戳递增的顺序,维护出现异象石的结点序列,并用一个变量ans来记录序列中相邻两个节点之间的路径长度之和(注意序列首位也看作是相邻的,此处要特殊处理)。

<----如果小可爱你还对set不是很了解,可以去看看我的另一篇博客鸭☟☟☟

这是一个时空漩涡...(说的我自己都信了(๑´ㅂ`๑) )

这道题我是用树上倍增法求LCA的,←点击颜色奇奇怪怪的部分即可查看相关资料啦(hhh我想的真周到ヽ( ̄▽ ̄)ノ)


 

话不多说,是时候展示真正的AC代码辽

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 const int N=1e6+5,inf=0x3f3f3f3f;
  4 long long next[2*N],head[2*N],to[N*2],d[N],f[N][21],xu[N],id[N],v[2*N],in[N],di[N];
  5 int n;
  6 int m,q,w,p,tot,cnt;
  7 long long ans;
  8 set<int>Set;
  9 int read()
 10 {
 11     int f=1;char ch;
 12     while((ch=getchar())<'0'||ch>'9')
 13         if(ch=='-')f=-1;
 14     int res=ch-'0';
 15     while((ch=getchar())>='0'&&ch<='9')
 16         res=res*10+ch-'0';
 17     return res*f;
 18 }
 19 void write(int x)
 20 {
 21     if(x<0)
 22     {
 23         putchar('-');
 24         x=-x;
 25     }
 26     if(x>9)write(x/10);
 27     putchar(x%10+'0');
 28 }
 29 void ADD(int x,int y,int z)
 30 {
 31     next[++tot]=head[x];
 32     head[x]=tot;
 33     to[tot]=y;v[tot]=z;
 34 }
 35 int lca(int x,int y)
 36 {
 37     if(d[x]<d[y])swap(x,y);
 38     for(int i=20;i>=0;i--)
 39     {
 40         if(d[f[x][i]]>=d[y])x=f[x][i];
 41         if(x==y)return x;
 42     }
 43     for(int i=20;i>=0;i--)
 44     {
 45         if(f[x][i]!=f[y][i])
 46         {
 47             x=f[x][i];
 48             y=f[y][i];
 49         }
 50     }
 51     return f[x][0];
 52 }
 53 long long dis(int x,int y)
 54 {
 55     return di[x]+di[y]-di[lca(x,y)]*2;
 56 }
 57 inline void dfs(int x,int fa)
 58     {
 59         id[++cnt]=x; xu[x]=cnt;
 60         f[x][0]=fa; d[x]=d[fa]+1;
 61         int i;
 62         for(i=head[x];i;i=next[i]) if(to[i]!=fa)
 63         {
 64             di[to[i]]=di[x]+v[i];
 65             dfs(to[i],x);
 66         }
 67         return;
 68     }
 69     inline void Pre()
 70     {
 71         int i,j;
 72         dfs(1,0);
 73         for(i=1;i<=19;i++)
 74         {
 75             for(j=1;j<=n;j++) f[j][i]=f[f[j][i-1]][i-1];
 76         }
 77         Set.insert(-inf); Set.insert(inf);
 78         return;
 79     }
 80 void ad(int x)
 81 {
 82     if(in[xu[x]])return ;
 83     Set.insert(xu[x]);in[xu[x]]=1;
 84     set<int>::iterator it=Set.upper_bound(xu[x]);
 85     int p2=(*it),p1=(*(--(--it)));
 86     if(p1!=-inf) ans+=dis(id[p1],x);
 87     if(p2!=inf)ans+=dis(x,id[p2]);
 88     if(p1!=-inf&&p2!=inf)ans-=dis(id[p1],id[p2]);
 89     //cout<<ans<<endl;注释掉的部分是调试的时候加上去的,不用理啦(*・ω-q) 
 90 }
 91 void de(int x)
 92 {
 93     if(!in[xu[x]])return ;Set.erase(xu[x]);in[xu[x]]=0;
 94     set<int>::iterator it=Set.upper_bound(xu[x]);
 95     int p2=(*it),p1=(*(--it));//这个地方千万不要写反哟,区别hin大的(才不会告诉你我自己都写错了呢 96     if(p1!=-inf) ans-=dis(id[p1],x);
 97     if(p2!=inf)ans-=dis(x,id[p2]);
 98     if((p1!=-inf)&&(p2!=inf))ans+=dis(id[p1],id[p2]);
 99     //cout<<ans<<endl;
100 }
101 long long an()
102 {
103     long long dd=(Set.size()>3)?(dis(id[*(Set.upper_bound(-inf))],id[*(--Set.lower_bound(inf))])):0;
104     //cout<<dd<<endl;上面的三目运算符应该看得懂吧,毕竟小可爱们都辣么聪明,拆成几个部分就比较好理解惹
105     return (ans+dd)>>1;//对首尾的特殊处理
106 }
107 int main()
108 {
109     n=read();
110     for(int i=1;i<n;i++)
111     {
112         int x,y,z;
113         x=read();y=read();z=read();
114         ADD(x,y,z);
115         ADD(y,x,z);
116     }
117     Pre();
118     //Set.insert(-inf); Set.insert(inf);
119     q=read();
120     while(q--)
121     {
122         char ch[5];int x;
123         scanf("%s",ch+1);//最好是这样写哦,不然很容易错鸭
124         switch(ch[1])
125         {
126             case '+':{
127                 x=read();
128                 ad(x);
129                 break;
130             }
131             case '-':{
132                 x=read();
133                 de(x);
134                 break;
135             }    
136             case '?':{
137                 printf("%lld",an());
138                 putchar('
');
139                 break;
140             }
141                 
142         }
143     }
144     return 0;
145 }

看我写的介么认真,就顺手给个“推荐”吧~φ(>ω<*) 爱心三连击❥❥❥(ゝω・✿ฺ)

原文地址:https://www.cnblogs.com/ljy-endl/p/11348887.html