码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2019-07-14 09:34:23      阅读:106      评论:0      收藏:0      [点我收藏+]

标签:16px   continue   tip   网络   条件   ali   size   $*   dep   

题干:

  这世界上有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 点双连通分量)

标签:16px   continue   tip   网络   条件   ali   size   $*   dep   

原文地址:https://www.cnblogs.com/OI-zzyy/p/11183044.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!