[bzoj3331] [BeiJing2013] 压力(tarjan 点双连通分量)
Posted oi-zzyy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[bzoj3331] [BeiJing2013] 压力(tarjan 点双连通分量)相关的知识,希望对你有一定的参考价值。
题干:
这世界上有N个网络设备,他们之间有M个双向的链接。这个世界是连通的。在一段时间里,有Q个数据包要从一个网络设备发送到另一个网络设备。一个网络设备承受的压力有多大呢?很显然,这取决于Q个数据包各自走的路径。不过,某些数据包无论走什么路径都不可避免的要通过某些网络设备。你要计算:对每个网络设备,必须通过(包括起点、终点)他的数据包有多少个?
对于40%的数据,N,M,Q≤2000
对于60%的数据,N,M,Q≤40000
对于100%的数据,N≤100000,M,Q≤200000
题解:
必须通过的路径?很容易就可以想到树上差分求解(在路径上都放一个价值为1的物品,答案就是每个点物品的总价值)。
但题干中并没有说这一定是一棵树,只是说是连通图。
那我们就需考虑几种情况:
1、起始节点与末尾节点都为割点:直接在起始节点与末尾节点差分
(怎么来的割点?不是走路径吗?其实我们差分的对象是几个点,针对于点,我们就应该想到点双连通分量)
2、起始节点与末尾节点有一个不为割点或都不为割点:
这就需要先找出起始节点或末尾节点所在环的割点,再在这个割点上进行差分。
(直接差分不行吗?题干中说的是“ 必须通过它的数据包有多少个 ”,若一个点在环上,那么它一定至少有两条路径可以走,就不满足“ 必须 ”这个条件)
考虑了这两种情况,这道题就有了一个比较完整的框架。但在真正解题时,我们采用缩点的方法来等效于上述
“找出起始节点或末尾节点所在环的割点,再在这个割点上进行差分”
(在最后的统计答案中,dfs序还是比较好用,省去了跑dfs统计答案)
tarjan 点双连通分量求法+缩点:
1 if(!dfn[to]) 2 tarjan(to); 3 low[x]=min(low[x],low[to]); 4 if(low[to]>=dfn[x]) 5 opt++; sum++; 6 if(x!=1||opt>1) cut[x]=1; 7 do 8 tmp=sta[up--]; 9 cir[tmp]=sum; 10 dcc[sum].push_back(tmp); 11 while(tmp!=to); 12 cir[x]=sum; 13 dcc[sum].push_back(x); 14 15 16 else low[x]=min(low[x],dfn[to]);
Code:(两种不同打法,第二种为正解,第一种打得较方便,但时间复杂度会高一些)
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<iostream> 5 #define $ 220010 6 using namespace std; 7 int m,n,q,first[$],tot1,dfn[$],low[$],tar,tip[$],second[$],tot2; 8 int dad[$][22],sta[$],up,ans[$],dep[$],sum,pre[$]; 9 struct tree int to,next; a[$*5],tr[$*5]; 10 inline int min(int x,int y) return x<y?x:y; 11 inline void swap(int &x,int &y) int t=x; x=y; y=t; 12 inline void add(int x,int y) 13 a[++tot1]=(tree) y,first[x] ; 14 first[x]=tot1; 15 a[++tot1]=(tree) x,first[y] ; 16 first[y]=tot1; 17 18 inline void addtr(int x,int y) 19 tr[++tot2]=(tree) y,second[x] ; 20 second[x]=tot2; 21 tr[++tot2]=(tree) x,second[y] ; 22 second[y]=tot2; 23 24 inline void tarjan(int x) 25 dfn[x]=low[x]=++tar; sta[++up]=x; 26 for(register int i=first[x],tmp;i;i=a[i].next) 27 int to=a[i].to; 28 if(!dfn[to]) 29 tarjan(to); 30 low[x]=min(low[x],low[to]); 31 if(low[to]>=dfn[x]) 32 ++sum; 33 do 34 tmp=sta[up--]; 35 addtr(sum,tmp); 36 while(tmp!=to); 37 addtr(sum,x); 38 39 40 else low[x]=min(low[x],dfn[to]); 41 42 43 inline void ready(int x) 44 pre[--pre[0]]=x; 45 for(register int i=second[x];i;i=tr[i].next) 46 int to=tr[i].to; 47 if(to==dad[x][0]) continue; 48 dad[to][0]=x; 49 for(register int j=1;j<=20;++j) dad[to][j]=dad[dad[to][j-1]][j-1]; 50 dep[to]=dep[x]+1; 51 ready(to); 52 53 54 inline int LCA(int x,int y) 55 if(dep[x]<dep[y]) swap(x,y); 56 for(register int i=20;i>=0;--i) 57 if(dep[dad[x][i]]>=dep[y]) x=dad[x][i]; 58 if(x==y) return x; 59 for(register int i=20;i>=0;--i) 60 if(dad[x][i]!=dad[y][i]) x=dad[x][i],y=dad[y][i]; 61 return dad[x][0]; 62 63 signed main() 64 scanf("%d%d%d",&n,&m,&q); sum=n; 65 for(register int i=1,x,y;i<=m;++i) scanf("%d%d",&x,&y),add(x,y); 66 tarjan(1); 67 pre[0]=sum+1; dep[1]=1; 68 ready(1); 69 for(register int i=1,x,y,lca;i<=q;++i) 70 scanf("%d%d",&x,&y); lca=LCA(x,y); 71 ++ans[x]; ++ans[y]; 72 --ans[lca]; --ans[dad[lca][0]]; 73 74 for(register int i=1,x;i<=sum;++i) 75 x=pre[i],ans[dad[x][0]]+=ans[x]; 76 for(register int i=1;i<=n;++i) printf("%d\n",ans[i]); 77
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<vector> 5 #define $ 220010 6 using namespace std; 7 int m,n,q,first[$],tot1,dfn[$],low[$],tar,tip[$],second[$],tot2,cut[$],cnt; 8 int dad[$*2][22],sta[$],up,ans[$],dep[$],sum,pre[$],cir[$],id[$],data[$]; 9 vector<int> dcc[$]; 10 struct tree int to,next; a[$*5],tr[$*5]; 11 inline int min(int x,int y) return x<y?x:y; 12 inline void swap(int &x,int &y) int t=x; x=y; y=t; 13 inline void add(int x,int y) 14 a[++tot1]=(tree) y,first[x] ; 15 first[x]=tot1; 16 a[++tot1]=(tree) x,first[y] ; 17 first[y]=tot1; 18 19 inline void addtr(int x,int y) 20 tr[++tot2]=(tree) y,second[x] ; 21 second[x]=tot2; 22 tr[++tot2]=(tree) x,second[y] ; 23 second[y]=tot2; 24 25 inline void tarjan(int x,int opt=0) 26 dfn[x]=low[x]=++tar; sta[++up]=x; 27 for(register int i=first[x],tmp;i;i=a[i].next) 28 int to=a[i].to; 29 if(!dfn[to]) 30 tarjan(to); 31 low[x]=min(low[x],low[to]); 32 if(low[to]>=dfn[x]) 33 opt++; sum++; 34 if(x!=1||opt>1) cut[x]=1; 35 do 36 tmp=sta[up--]; 37 cir[tmp]=sum; 38 dcc[sum].push_back(tmp); 39 while(tmp!=to); 40 cir[x]=sum; 41 dcc[sum].push_back(x); 42 43 44 else low[x]=min(low[x],dfn[to]); 45 46 47 inline void ready(int x) 48 pre[--pre[0]]=x; 49 for(register int i=second[x];i;i=tr[i].next) 50 int to=tr[i].to; 51 if(to==dad[x][0]) continue; 52 dad[to][0]=x; 53 for(register int j=1;j<=20;++j) dad[to][j]=dad[dad[to][j-1]][j-1]; 54 dep[to]=dep[x]+1; 55 ready(to); 56 57 58 inline int LCA(int x,int y) 59 if(dep[x]<dep[y]) swap(x,y); 60 for(register int i=20;i>=0;--i) 61 if(dep[dad[x][i]]>=dep[y]) x=dad[x][i]; 62 if(x==y) return x; 63 for(register int i=20;i>=0;--i) 64 if(dad[x][i]!=dad[y][i]) x=dad[x][i],y=dad[y][i]; 65 return dad[x][0]; 66 67 signed main() 68 scanf("%d%d%d",&n,&m,&q); cnt=n; 69 for(register int i=1,x,y;i<=m;++i) scanf("%d%d",&x,&y),add(x,y); 70 tarjan(1); 71 for(register int i=1;i<=n;++i) if(cut[i]) cir[i]=id[i]=++cnt; 72 for(register int i=1;i<=sum;++i) 73 for(register int j=0;j<dcc[i].size();++j) 74 int to=dcc[i][j]; 75 if(cut[to]) addtr(i,id[to]); 76 77 pre[0]=cnt+1; dep[1]=1; 78 ready(1); 79 for(register int i=1,x,y,lca;i<=q;++i) 80 scanf("%d%d",&x,&y); 81 if(!cut[x]) data[x]++; 82 if(!cut[y]) data[y]++; 83 x=cir[x], y=cir[y]; 84 lca=LCA(x,y); 85 ++ans[x]; ++ans[y]; 86 --ans[lca]; --ans[dad[lca][0]]; 87 88 for(register int i=1,x;i<=cnt;++i) 89 x=pre[i],ans[dad[x][0]]+=ans[x]; 90 for(register int i=1;i<=n;++i) printf("%d\n",cut[i]?ans[cir[i]]:data[i]); 91
以上是关于[bzoj3331] [BeiJing2013] 压力(tarjan 点双连通分量)的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ3331[BeiJing2013]压力 Tarjan求点双