蓝皮书:异象石 【dfs序+lca】

题目详见蓝皮书【算法竞赛:进阶指南】。

 


题目大意:

就是给你一颗树,然后我们要在上面进行三种操作: 

 1.标记某个点  或者  2.撤销某个点的标记  以及   3.询问标记点在树上连通所需的最短总边权

 

 


数据范围:

点数以及操作数:1e5,边权:1e9(意思就是答案要 long long 存)。

 

 

 


分析:

这道题比赛的时候看的是真懵逼。。。

 

表示只会 n^3 做法(最多会n^2),以及特殊形态(比如链或者菊花图)的骗分法。

 

然鹅正解大概是 $O(n log n)$  的做法,和树搭上了关系,加上这数据范围...

 

于是正解真是这个复杂度。(看到标程的时候表示惊讶,我太弱了)

 

标算就是用的dfs序加上lca的算法(如题)

 

首先我们不考虑 2、3 操作,我们先考虑如果树上有 k 个点被标记了,我们要得到树上 k 个点连通图的最小总边权。

我们可以在纸上画出这棵树以及标记的点,然后我们从左到右把点连成一块。

这时我们发现每两个相邻的点之间(相当于dfs序)的距离加上最后一个点和第一个点的距离,和正好是答案的两倍。

也就是说,如果把这些标记点按照dfs序排成一列,首尾相连形成一个环的话,答案就是这个环相邻点距离之和除以二。

(这种东西考场上怎么做得出来嘛)

 

那么我们回到原题,如果用上面的方法暴力处理答案,那么复杂度是 $O(n^{2} log n)$ 的(还不如 我自己想到的 n^2 咧)。

那么我们结合加点删点特殊的性质,以此优化算法复杂度。

我们发现加点其实就是在原环两相邻点之间插入了一个新点,然后原来两个相邻点的贡献没了,但是多了新点与这两个点分别的贡献。

那么删点类似的,就是令原环中某个点与相邻的两个点之间的贡献删除,并且多了这两个点之间的距离的贡献。

 

于是我们就可以用一个set来维护标记点,然后每次求距离的时候要用到 lca。

(lca建议常数小的树剖,倍增效率感人,tarjan 的话比较冷门基本不考虑)

 

那么我们就可以愉快地敲代码了!

 


 

代码 :

 

 1 //by Judge
 2 #include<bits/stdc++.h>
 3 #pragma GCC optimize(2)
 4 #define It set<int>::iterator
 5 #define ll long long
 6 using namespace std;
 7 const int M=1e5+11;
 8 #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
 9 char buf[1<<21],*p1,*p2;
10 inline int read(){ int x=0;
11     char c=getchar(); while(!isdigit(c)) c=getchar();
12     for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x;
13 } inline int cread(){ char c=getchar();
14     while(c!='+'&&c!='-'&&c!='?') c=getchar();
15     return c=='?'?1:(c=='+'?2:3);
16 } char sr[1<<21],z[20];int C=-1,Z;
17 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
18 inline void print(ll x,char chr='\n'){
19     if(C>1<<20)Ot(); while(z[++Z]=x%10+48,x/=10);
20     while(sr[++C]=z[Z],--Z);sr[++C]=chr;
21 } int n,m,pat,tim,head[M],f[M],son[M]; ll ans,dis[M];
22 int siz[M],top[M],dep[M],dfn[M],p[M]; set<int> s;
23 struct Edge{ int to,val,nxt;
24     Edge(int v,int c,int x):to(v),val(c),nxt(x){} Edge(){}
25 }e[M<<1];
26 inline void add(int u,int v,int c){
27     e[++pat]=Edge(v,c,head[u]),head[u]=pat;
28     e[++pat]=Edge(u,c,head[v]),head[v]=pat;
29 }
30 #define v e[i].to
31 void dfs(int u,int fa){
32     siz[u]=1,f[u]=fa,dep[u]=dep[fa]+1;
33     for(int i=head[u];i;i=e[i].nxt) if(v^fa){
34         dis[v]=dis[u]+e[i].val,
35         dfs(v,u),siz[u]+=siz[v];
36         if(siz[v]>siz[son[u]]) son[u]=v;
37     }
38 } void dfs(int u){
39     dfn[u]=++tim,p[tim]=u;
40     if(!top[u]) top[u]=u; if(!son[u]) return ;
41     top[son[u]]=top[u],dfs(son[u]);
42     for(int i=head[u];i;i=e[i].nxt)
43         if(v^f[u]&&v^son[u]) dfs(v);
44 }
45 #undef v
46 inline int lca(int u,int v){
47     while(top[u]^top[v])
48         (dep[top[u]]>dep[top[v]])?
49             u=f[top[u]]:v=f[top[v]];
50     return dep[u]<dep[v]?u:v;
51 } inline ll get(int u,int v){
52     return dis[u]+dis[v]-dis[lca(u,v)]*2;
53 } inline It L(It it){
54     return (it==s.begin())?--s.end():--it;
55 } inline It R(It it){
56     return (it==--s.end())?s.begin():++it;
57 }
58 int main(){
59     n=read();
60     for(int i=1,u,v,c;i<n;++i)
61         u=read(),v=read(),
62         c=read(),add(u,v,c);
63     dfs(1,0),dfs(1),m=read();
64     for(int opt,x,t;m;--m){
65         opt=cread(); It it;
66         if(opt==1) print(ans/2);
67         else if(opt==2){ x=read();
68             if(s.size()){
69                 it=s.lower_bound(dfn[x]);
70                 if(it==s.end()) it=s.begin(); t=*L(it);
71                 ans+=get(x,p[t])+get(x,p[*it])-get(p[t],p[*it]);
72             } s.insert(dfn[x]);
73         } else if(opt==3){ x=read();
74             if(s.size()>1){
75                 it=s.find(dfn[x]),t=*L(it),it=R(it);
76                 ans-=get(x,p[t])+get(x,p[*it])-get(p[t],p[*it]);
77             } s.erase(dfn[x]);
78         }
79     } return Ot(),0;
80 }

 

posted @ 2018-10-31 13:17  Jμdge  阅读(351)  评论(0编辑  收藏  举报