[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 }

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

时间: 2024-10-11 10:44:51

[bzoj3331] [BeiJing2013] 压力(tarjan 点双连通分量)的相关文章

【BZOJ3331】[BeiJing2013]压力 Tarjan求点双

[BZOJ3331][BeiJing2013]压力 Description 如今,路由器和交换机构建起了互联网的骨架.处在互联网的骨干位置的核心路由器典型的要处理100Gbit/s的网络流量.他们每天都生活在巨大的压力之下. 小强建立了一个模型.这世界上有N个网络设备,他们之间有M个双向的链接.这个世界是连通的.在一段时间里,有Q个数据包要从一个网络设备发送到另一个网络设备. 一个网络设备承受的压力有多大呢?很显然,这取决于Q个数据包各自走的路径.不过,某些数据包无论走什么路径都不可避免的要通过

poj3352 road construction tarjan求双连通分量

填坑--链接:http://poj.org/problem?id=3352 题意:求出图中再加上几条边会全部边双连通. 思路大概就是求出图中所有的双连通分量,然后像$SCC$一样缩点,缩完后每两个双连通分量再连边即可. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int maxn

codeforces CF732F Tourist Reform Tarjan边双连通分量

$ \rightarrow $ 戳我进CF原题 一张有向图中,设 $ r_i $ 为从点 $ i $ 出发能够到达的点的数量. 定义有向图的"改良值"为 $ r_i $ 的最小值. 现给出一张无向图,要求给每条边定一个方向,使产生的有向图"改良值"最大. $ n,m \le 400000 $ 对于无向图的每个"边双连通分量",一定存在一种定向方法,使其改良值等于其大小 把无向图缩点后,以最大的 $ e-DCC $ 为零出度点(终点) $ BFS

【BZOJ-1123】BLO Tarjan 点双连通分量

1123: [POI2008]BLO Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 970  Solved: 408[Submit][Status][Discuss] Description Byteotia城市有n个 towns m条双向roads. 每条 road 连接 两个不同的 towns ,没有重复的road. 所有towns连通. Input 输入n<=100000 m<=500000及m条边 Output 输出n个数,代表如果把第

BZOJ3331: [BeiJing2013]压力

传送门 Tarjan的三大应用之一:求解点双联通分量. 求解点双联通分量.然后缩点,差分优化即可. //BZOJ 3331 //by Cydiater //2016.10.29 #include <iostream> #include <cmath> #include <cstdlib> #include <cstdio> #include <queue> #include <map> #include <cstring>

tarjan 边双连通分量 对点进行分组 每组点都在一个双连通分量里边

int dfn[N],low[N],id[N],s[N],p,num,t,son[N];//dfn记录dfs时间戳//low代表当前点到达的最小时间戳,id对点进行分组编号.num是时间戳//s临时存储数据的手工栈,p栈顶元素的位置,son记录儿子因为无向图记录边都是两个边 void tarjan(int pos){ dfn[pos]=low[pos]=++num; s[++p]=pos; for(edge *it=adj[pos];it;it=it->next){ if(!dfn[it->i

连通分量模板:tarjan: 求割点 &amp;&amp; 桥 &amp;&amp; 缩点 &amp;&amp; 强连通分量 &amp;&amp; 双连通分量 &amp;&amp; LCA(最近公共祖先)

PS:摘自一不知名的来自大神. 1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点. 2.割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个连通块,就称这个点集为割点集合. 3.点连通度:最小割点集合中的顶点数. 4.割边(桥):删掉它之后,图必然会分裂为两个或两个以上的子图. 5.割边集合:如果有一个边集合,删除这个边集合以后,原图变成多个连通块,就称这个点集为割边集合. 6.边连通度:一个图的边连通度的定义为,最

(转)Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)

本文转载自:http://hi.baidu.com/lydrainbowcat/item/f8a5ac223e092b52c28d591c 作者提示:在阅读本文之前,请确保您已经理解并掌握了基本的Tarjan算法,不会的请到http://hi.baidu.com/lydrainbowcat/blog/item/42a6862489c98820c89559f3.html阅读.   基本概念:   1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点. 2.割点集合:在一个无向连通图中,如

POJ 3352 Road Construction(边双连通分量,桥,tarjan)

题解转自http://blog.csdn.net/lyy289065406/article/details/6762370   文中部分思路或定义模糊,重写的红色部分为修改过的. 大致题意: 某个企业想把一个热带天堂岛变成旅游胜地,岛上有N个旅游景点,保证任意2个旅游景点之间有路径连通的(可间接连通).而为了给游客提供更方便的服务,该企业要求道路部门在某些道路增加一些设施. 道路部门每次只会选择一条道路施工,在该条道路施工完毕前,其他道路依然可以通行.然而有道路部门正在施工的道路,在施工完毕前是