BZOJ3331 [BeiJing2013]压力[圆方树+树上差分]

圆方树新技能get。具体笔记见图连通性问题学习笔记。

这题求无向图的必经点,这个是一个固定套路:首先,一张连通的无向图中,每对点双和点双之间是以一个且仅一个割点连接起来的(如果超过一个就不能是割点了),那么,在一个点双内部,从出发点开始,要走到另外一个点双中,这个中间的割点就是一条必经之路(没有其他路可以绕,否则这就有一个环了),所以,路上所有割点都是必经点,而点双内部走的话,由点双的定义,是至少有两条点不相交的路径的(当然两个点的点双的话直接没有中间点),所以中间非割点是可经而不是必经的。

所以,构建圆方树,起始点和终止点构成的路径上所有圆点就是答案。然后,依题意做树上差分即可。

为什么我线性求lca还跑不过log的啊。。

圆方树构建的code大致是模仿zsy写的。。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 #include<queue>
 7 #define mst(x) memset(x,0,sizeof x)
 8 #define dbg(x) cerr << #x << " = " << x <<endl
 9 #define dbg2(x,y) cerr<< #x <<" = "<< x <<"  "<< #y <<" = "<< y <<endl
10 using namespace std;
11 typedef long long ll;
12 typedef double db;
13 typedef pair<int,int> pii;
14 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
15 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
16 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;}
17 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;}
18 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
19 template<typename T>inline T read(T&x){
20     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
21     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
22 }
23 const int N=1e5+7;
24 struct thxorz{
25     int to[N<<2],nxt[N<<2],head[N<<1],tot;
26     inline void add(int x,int y){
27         to[++tot]=y,nxt[tot]=head[x],head[x]=tot;
28         to[++tot]=x,nxt[tot]=head[y],head[y]=tot;
29     }
30 }G1,G2,Q;
31 int n,m,q;
32 int tag[N<<1],fa[N<<1],anc[N<<1],vis[N<<1];
33 int Find(int x){return anc[x]==x?x:anc[x]=Find(anc[x]);}
34 #define y G2.to[j]
35 #define qy Q.to[j]
36 void tarjan_lca(int x,int fat){
37     anc[x]=x;fa[x]=fat;
38     for(register int j=G2.head[x];j;j=G2.nxt[j])if(fat^y)tarjan_lca(y,x),anc[y]=x;
39     vis[x]=1;int tmp;
40     for(register int j=Q.head[x];j;j=Q.nxt[j])if(vis[qy])tmp=Find(qy),--tag[tmp],--tag[fa[tmp]];
41 }
42 void dfs(int x){for(register int j=G2.head[x];j;j=G2.nxt[j])if(fa[x]^y)dfs(y),tag[x]+=tag[y];}
43 #undef y
44 #undef qy
45 #define y G1.to[j]
46 int dfn[N],low[N],stk[N],Top,tim,cnt;
47 void tarjan(int x){//promise:connected,without vertexes isolated.
48     dfn[x]=low[x]=++tim;stk[++Top]=x;
49     for(register int j=G1.head[x];j;j=G1.nxt[j]){
50         if(!dfn[y]){//We needn't get the cut-vertexes,and cut-vertexes have nothing to do with v-DCCs.
51             tarjan(y),MIN(low[x],low[y]);
52             if(dfn[x]==low[y]){
53                 int tmp;++cnt;
54                 do tmp=stk[Top--],G2.add(tmp,cnt);while(tmp^y);
55                 G2.add(x,cnt);
56             }
57         }
58         else MIN(low[x],dfn[y]);
59     }
60 }
61 #undef y
62 int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout);
63     cnt=read(n),read(m),read(q);
64     for(register int i=1,x,y;i<=m;++i)read(x),read(y),G1.add(x,y);
65     for(register int i=1;i<=n;++i)if(!dfn[i])Top=0,tarjan(i);
66     for(register int i=1,x,y;i<=q;++i)read(x),read(y),Q.add(x,y),++tag[x],++tag[y];
67     tarjan_lca(1,0);dfs(1);
68     for(register int i=1;i<=n;++i)printf("%d
",tag[i]);
69     return 0;
70 }
View Code

总结:图中简单路径问题,经过点的统计问题,考虑从点双和圆方树入手。

顺带一提,点双缩点一般配合圆方树食用,而边双直接缩成一棵树,比如这题如果问必经边的话,一定是割边是必经边(证明同理),只要缩边双后在树上直接差分即可。

原文地址:https://www.cnblogs.com/saigyouji-yuyuko/p/11719529.html