[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 
View Code

 

技术图片
 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 
View Code

 

以上是关于[bzoj3331] [BeiJing2013] 压力(tarjan 点双连通分量)的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ3331[BeiJing2013]压力 Tarjan求点双

[bzoj3331] [BeiJing2013] 压力(tarjan 点双连通分量)

bzoj3330: [BeiJing2013]分数

bzoj 2553: [BeiJing2011]禁忌

bzoj2251 [2010Beijing Wc]外星联络

BZOJ 2460: [BeiJing2011]元素