血帆海盗
Posted moyiii
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了血帆海盗相关的知识,希望对你有一定的参考价值。
问题描述
随着资本的扩大,藏宝海湾贸易亲王在卡利姆多和东部王国大陆各建立了N/2 个港口。大灾变发生以后,这些港口之间失去了联系,相继脱离了藏宝海湾贸易亲王的管辖,各自为政。利益的驱动使得每个港口都想和对岸大陆的另一个港口建立贸易合作关系,由于地理位置因素,只有存在直接到达的航线的两个港口才能建立合作,而且每个港口只与对岸一个港口建立合作,因此并不是所有的港口都能找到合作伙伴。
血帆海盗得知这一消息以后,决定对其中一条航线进行干扰性的掠夺。经过分析,血帆海盗计算出最多能有W 对港口合作。如果两个港口之间只有一条航线,而且这条航线恰好是血帆海盗要掠夺的航线,这两个港口将不能建立合作关系。血帆海盗指挥官菲尔拉伦想知道他们有几种选择,可以让地精们无法建立W 对港口。
输入格式
第1行,两个整数N,M,表示一共的港口个数和航线条数。
接下来M行,每行两个整数A,B,表示卡利姆多的港口A与东部王国的港口B之间有一条航线直接连接,其中1<=A<=N/2,N/2+1<=B<=N。
输出格式
一个整数,表示血帆海盗可以选择掠夺的航线条数。
解释:如果掠夺一条航线以后,地精依然可以建立起最多的W个合作关系(可以有多种),
那么这条航线是不值得掠夺的,否则就是掠夺方案之一。
样例输入
8 5
1 5
1 6
2 7
3 7
4 8
样例输出
1
样例说明
地精做多能建立起合作关系的数量为3,掠夺(4,8)这条航线后,最多能建立的合作关系的
数量减少为2。
数据规模
40%的数据满足2<=N<=200,1<=M<=1000
100%的数据满足2<=N<=100000,1<=M<=100000,保证N为偶数
by BYVoid
【题解】
网络流第一道比较有难度的题,毁半下午一晚上,不过收获还不错。网络流当然没有什么好说的,明显点和边都给出来了,建一个原点一个汇点即可。关键是怎样在网络流的基础上找出能“破坏”最大流的关键边。
一开始的想法是在dinic的板子里直接做变动,记录那些满载边(类似最小瓶颈生成树?)。但是首先,改板子这个事本来就不靠谱(比如KMP的动物园那个题),其次一个边没有被改动过不能代表它不重要,这道题的边权只代表存在与否,所以全是01满载也没有意义。然后就开始在剩余图上做文章,想用并查集标记一下最大流里的边两端是否仍连通。发现这样一来只要有一个还连着整个图就和合一气了,明显不靠谱。然后开始艰辛的dfs,从源点搜反图从汇点搜正图,后来还是搜得很凌乱,调出样例交一下一个点都没有A。
最后还是非常虚地看了学长代码(这题网上根本没有题解啊摔,果然还是我太弱?)。发现用tarjan缩一下点好像问题就解决了?开开心心地继续去顺着tarjan的思路想。最近tarjan打得很多,不用板子就背下来了可喜可贺可喜可贺。但是为什么成立呢……调出来当时讲课的ppt看了很多回强连通分量的定义,每两个点之间都有路径可达,不管是环还是别的什么满足条件的图都可以,用的最多的还是环吧。因为网络流里正边载重反边就会连通,如果这条线路是可以替换的话正好和源点或汇点一起连成一个强连通分量,不可替换的边则两端会分属不同的强连通分量。一遍tarjan缩点之后枚举给出的每一条边,如果一个被使用的边(表现为边权为0)两端在一个强连通分量里,它没有“打击的意义”,把答案从最大流的基础上-1。所以说,我打了大半个晚上煞费苦心地在tarjan里区分正边反边,再在枚举时刨掉一大堆特殊情况,都是为了什么呀……果然还是dfs时留下的思维定式太深,在后面对tarjan的理解就不够透彻。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<queue> 5 #include<stack> 6 using namespace std; 7 const int sj=100005; 8 int n,m,h[sj],e,a1,a2,mid,jg,dep[sj]; 9 int ge,dfn[sj],low[sj],cnt,c[sj]; 10 struct B 11 { 12 int ne,v,w,u; 13 }b[sj*4]; 14 bool r[sj]; 15 void add(int x,int y,int z) 16 { 17 b[e].v=y; 18 b[e].w=z; 19 b[e].u=x; 20 b[e].ne=h[x]; 21 h[x]=e++; 22 } 23 void init() 24 { 25 scanf("%d%d",&n,&m); 26 memset(h,-1,sizeof(h)); 27 mid=n>>1; 28 for(int i=1;i<=m;i++) 29 { 30 scanf("%d%d",&a1,&a2); 31 add(a1,a2,1); 32 add(a2,a1,0); 33 r[a1]=1; 34 r[a2]=1; 35 } 36 for(int i=1;i<=mid;i++) 37 if(r[i]) add(0,i,1),add(i,0,0); 38 for(int i=mid+1;i<=n;i++) 39 if(r[i]) add(i,n+1,1),add(n+1,i,0); 40 memset(r,0,sizeof(r)); 41 } 42 queue<int> q; 43 int bfs(int x) 44 { 45 memset(dep,0,sizeof(dep)); 46 dep[x]=1; 47 while(!q.empty()) q.pop(); 48 q.push(x); 49 while(!q.empty()) 50 { 51 x=q.front(); 52 q.pop(); 53 for(int i=h[x];i!=-1;i=b[i].ne) 54 if(!dep[b[i].v]&&b[i].w) 55 { 56 dep[b[i].v]=dep[x]+1; 57 if(b[i].v==n+1) return 1; 58 q.push(b[i].v); 59 } 60 } 61 return 0; 62 } 63 int bj(int x,int y) 64 { 65 return x<y?x:y; 66 } 67 int dfs(int u,int f) 68 { 69 int ans=0; 70 if(u==n+1) return f; 71 for(int i=h[u],d;i!=-1;i=b[i].ne) 72 if(b[i].w&&dep[b[i].v]>dep[u]) 73 { 74 d=dfs(b[i].v,bj(f,b[i].w)); 75 f-=d; 76 ans+=d; 77 b[i].w-=d; 78 b[i^1].w+=d; 79 } 80 if(!ans) dep[u]=-1; 81 return ans; 82 } 83 stack<int> p; 84 int tarjan(int x) 85 { 86 dfn[x]=low[x]=++cnt; 87 p.push(x); 88 r[x]=1; 89 for(int i=h[x];i!=-1;i=b[i].ne) 90 { 91 if(!b[i].w) continue; 92 if(!dfn[b[i].v]) 93 { 94 tarjan(b[i].v); 95 low[x]=bj(low[x],low[b[i].v]); 96 } 97 else if(r[b[i].v]) 98 low[x]=bj(low[x],dfn[b[i].v]); 99 } 100 if(dfn[x]==low[x]) 101 { 102 int ww; 103 ge++; 104 do 105 { 106 ww=p.top(); 107 p.pop(); 108 r[ww]=0; 109 c[ww]=ge; 110 }while(ww!=x); 111 } 112 } 113 int main() 114 { 115 init(); 116 while(bfs(0)) jg+=dfs(0,0x3fff); 117 memset(r,0,sizeof(r)); 118 for(int i=1;i<=n;i++) 119 if(!dfn[i]) tarjan(i); 120 for(int i=1;i<=mid;i++) 121 for(int j=h[i];j!=-1;j=b[j].ne) 122 if(!b[j].w&&b[j].v!=0&&c[b[j].u]==c[b[j].v]) 123 jg--; 124 printf("%d",jg); 125 return 0; 126 }
以上是关于血帆海盗的主要内容,如果未能解决你的问题,请参考以下文章