首页 > 代码库 > 【ZJOI2004】嗅探器

【ZJOI2004】嗅探器

练tarjian不错的题,连WA几次后终于会记住tarjian的模板了技术分享

原题:

 某军搞信息对抗实战演习.红军成功地侵入了蓝军的内部网络.蓝军共有两个信息中心.红军计划在某台中间服务器上安装一个嗅探器,从而能够侦听到两个信息中心互相交换的所有信息.但是蓝军的网络相当的庞大,数据包从一个信息中心传到另一个信息中心可以不止有一条通路.现在需要你尽快地解决这个问题.应该把嗅探器安装在哪个中间服务器上才能保证所有的数据包都能被捕获?

1<=n<=100000

 

恩,首先可以确定答案一定是割顶中的某个点,必要性:因为删掉这个点后s和t就不连通了,所以这个点是割点,但是这个条件不充分,比如下面这个图:

技术分享

1也是割顶,但是删掉1后s和t依旧联通,也就是说某个割顶可能并不在s到t的必经路上

然而tarjian求出的dfn和low当然不能只拿来求割顶和强连通分量,用处还是挺广泛的

做法:在tarjian中,如果某点x,连接x的某条边的另一端点y,满足x!=s && low[y]>=dfn[x] && dfn[y]<=dfn[t] && low[t]>=dfn[x]

这四个条件的意义分别是:x当然不能是s;x是割点;y要在t之前遍历到;t走第二条路径到不了x(这几个条件描述有点奇怪,看不懂的无视就好= =)

其它几个条件都好理解,y要在t之前遍历到,也就是low[y]>=dfn[x]这个条件我想了很久(而且是试对的)

为什么呐:

充分性:如果x在y之后遍历,则y已经通过某条路径从s到t了(就是tarjian的dfs路径),删掉x后s依旧可以通过这个路径到t,x就不是所求的点

然后就是tarjian的模板辣

小技巧:因为只求最小的答案,所以不用把答案都存下来,ans=min(ans,x);即可

似乎用费用流也可以做?拆点,入点和出点之间权值为1,费用为点的序号,原图中边权值为oo,从s到t费用流,好像是对的?

代码:

技术分享
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 using namespace std;
 7 int read(){int z=0,mark=1;  char ch=getchar();
 8     while(ch<0||ch>9){if(ch==-)mark=-1;  ch=getchar();}
 9     while(ch>=0&&ch<=9){z=(z<<3)+(z<<1)+ch-0;  ch=getchar();}
10     return z*mark;
11 }
12 struct ddd{int next,y;}e[2100000];  int LINK[110000],ltop=0;
13 inline void insert(int x,int y){e[++ltop].next=LINK[x];LINK[x]=ltop;e[ltop].y=y;}
14 int n,s,t;
15 int dfn[110000],low[110000],dfs_cnt=0;
16 bool visited[110000];
17 int ans=999999999;
18 void tarjian(int x,int _father){
19     visited[x]=true;
20     dfn[x]=low[x]=++dfs_cnt;
21     for(int i=LINK[x];i;i=e[i].next){
22         if(!visited[e[i].y]){
23             tarjian(e[i].y,x);
24             low[x]=min(low[x],low[e[i].y]);
25             if(x!=s && low[e[i].y]>=dfn[x] && dfn[e[i].y]<=dfn[t] && low[t]>=dfn[x])  ans=min(ans,x);
26         }
27         else if(e[i].y!=_father)  low[x]=min(low[x],dfn[e[i].y]);
28     }
29 }
30 int main(){
31     freopen("ddd.in","r",stdin);
32     freopen("ddd.out","w",stdout);
33     memset(visited,0,sizeof(visited));
34     cin>>n;
35     int _left,_right;
36     for(;;){
37         _left=read(),_right=read();
38         if(!_left && !_right)  break;
39         insert(_left,_right),insert(_right,_left);
40     }
41     cin>>s>>t;
42     tarjian(s,0);
43     if(ans>n)  cout<<"No solution"<<endl;
44     else  cout<<ans<<endl;
45     return 0;
46 }
View Code

 

【ZJOI2004】嗅探器