题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3258

因为只走最短路,所以先正反两遍djkstra,新建边。

  这里的边是单向边。所以要用原来的边的话不仅要把反向边打标记,还要把反向边的流量改成0!

  但最后枚举边判flag的时候只看非反向边,如果新建边就能从2开始+=2遍历。不然无法区分是不是反向边。所以别用原来的边了吧。

求最小割得到一组可行解。流量当然就是两端点中权值较小的点的权值。

重点:

  在残量网络上把边双连通分量缩点。剩下的边只有满流的。

  1)非满流边:说明在S-T连通性方面等价于它的其他边的流量更小。所以它不会出现在任何可行解中。

  2)满流边,两端点在同一个SCC中:说明两端点所在的连通块之间有非满流边,即两端点所在连通块之间的边的流量和大于两边的流量,此时应割掉两边的边;

          所以两连通块之间的边不在任一可行解中,包括这条边。

  3)满流边,两端点不在同一个SCC中,且不是直接连通S和T所在连通块:这说明它是在S所在块到T所在块的路径链上的一条边。

                  这条路径上每两个点之间的边的流量总和各各相等 一样的感觉吧。反正路径上可以任选一个S-T割,方案就是多种的。

  4)满流边,两端点不在同一个SCC中,且直接连通S和T所在的连通块:比如,增大它的流量,最大流的值会变大。它是必须被割的边。

jcvb(金策)的解读:

在残余网络上跑tarjan求出所有SCC,记id[u]为点u所在SCC的编号。显然有id[s]!=id[t](否则s到t有通路,能继续增广)。

①对于任意一条满流边(u,v),(u,v)能够出现在某个最小割集中,当且仅当id[u]!=id[v];
②对于任意一条满流边(u,v),(u,v)必定出现在最小割集中,当且仅当id[u]==id[s]且id[v]==id[t]。

<==将每个SCC缩成一个点,得到的新图就只含有满流边了。那么新图的任一s-t割都对应原图的某个最小割,从中任取一个把id[u]和id[v]割开的割即可证明。


<==:假设将(u,v)的边权增大,那么残余网络中会出现s->u->v->t的通路,从而能继续增广,于是最大流流量(也就是最小割容量)会增大。这即说明(u,v)是最小割集中必须出现的边。

(求SCC的正确姿势!)

(输出单词的 仅首字母大写)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define ll long long
using namespace std;
const int N=,M=;
const ll INF=5e12;
int T,n,m,head[N],cur[N],xnt,dfn[N],low[N],tot,col[N],cnt,stack[N],top;
ll dis[][N],a[N],mxflow;
bool vis[N],bri[M<<],flag,ins[N];
struct Edge{
int next,to;ll cap,w;
Edge(int n=,int t=,ll c=,ll w=):next(n),to(t),cap(c),w(w) {}
}edge[M<<],ed[M<<];
int rdn()
{
int ret=;char ch=getchar();
while(ch>''||ch<'')ch=getchar();
while(ch>=''&&ch<='')(ret*=)+=ch-'',ch=getchar();
return ret;
}
int rdl()
{
ll ret=;char ch=getchar();
while(ch>''||ch<'')ch=getchar();
while(ch>=''&&ch<='')(ret*=)+=ch-'',ch=getchar();
return ret;
}
void add(int x,int y,ll w)
{
edge[++xnt]=Edge(head[x],y,,w);head[x]=xnt;
edge[++xnt]=Edge(head[y],x,,w);head[y]=xnt;
}
void adde(int x,int y)
{
ll z=min(a[x],a[y]);
ed[++xnt]=Edge(head[x],y,z,);head[x]=xnt;
ed[++xnt]=Edge(head[y],x,,);head[y]=xnt;
}
void dj(int d)
{
memset(dis[d],,sizeof dis[d]);dis[d][d?n:]=;
memset(vis,,sizeof vis);
priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > > q;
q.push(make_pair(,d?n:));
while(q.size())
{
int k=q.top().second;q.pop();
while(vis[k]&&q.size())k=q.top().second,q.pop();
if(vis[k])break;vis[k]=;
for(int i=head[k],v;i;i=edge[i].next)
if(dis[d][v=edge[i].to]>dis[d][k]+edge[i].w)
{
dis[d][v]=dis[d][k]+edge[i].w;q.push(make_pair(dis[d][v],v));
}
}
}
void init()
{
dj();dj();int xt=xnt;xnt=;memset(head,,sizeof head);
for(int i=;i<=xt;i++)
if(dis[][edge[i^].to]+dis[][edge[i].to]+edge[i].w==dis[][n])adde(edge[i^].to,edge[i].to);
}
bool bfs()
{
memset(dfn,,sizeof dfn);dfn[]=;
queue<int> q;q.push();
while(q.size())
{
int k=q.front();q.pop();
for(int i=head[k],v;i;i=ed[i].next)
if(!dfn[v=ed[i].to]&&ed[i].cap)
{dfn[v]=dfn[k]+;if(v==n)return true;q.push(v);}
}
return false;
}
ll dinic(int k,ll flow)
{
if(k==n)return flow;//////
ll use=;
for(int& i=cur[k],v;i;i=ed[i].next)
if(dfn[v=ed[i].to]==dfn[k]+&&ed[i].cap)
{
ll tmp=dinic(v,min(ed[i].cap,flow-use));
if(!tmp)dfn[v]=;
ed[i].cap-=tmp;ed[i^].cap+=tmp;use+=tmp;
if(use==flow)break;
}
return use;
}
//void dfs(int cr)
//{
// dfn[cr]=low[cr]=++tot;
// for(int i=head[cr],v;i;i=ed[i].next)
// if(ed[i].cap)
// {
// if(dfn[v])low[cr]=min(low[cr],dfn[v]);
// else{dfs(v);low[cr]=min(low[cr],low[v]);}
// if(low[v]>dfn[cr])bri[i]=1;
// }
//}
//void dfs2(int cr)
//{
// col[cr]=cnt;
// for(int i=head[cr];i;i=ed[i].next)
// if(ed[i].cap&&!bri[i]&&!col[ed[i].to])dfs2(ed[i].to);
//}
//void tarjan()
//{
// memset(col,0,sizeof col);memset(bri,0,sizeof bri);memset(dfn,0,sizeof dfn);
// cnt=0;tot=0;dfs(1);
// for(int i=1;i<=n;i++)if(!col[i])cnt++,dfs2(i);
//}
void dfs(int cr)
{
dfn[cr]=low[cr]=++tot;stack[++top]=cr;ins[cr]=;
for(int i=head[cr],v;i;i=ed[i].next)
if(ed[i].cap)
{
if(ins[v=ed[i].to])low[cr]=min(low[cr],dfn[v]);
else if(!dfn[v])//所以自己必须赋值为0
{dfs(v);low[cr]=min(low[cr],low[v]);}
}
if(dfn[cr]==low[cr])
{
cnt++;while(stack[top]!=cr)ins[stack[top]]=,col[stack[top--]]=cnt;
ins[stack[top]]=;col[stack[top--]]=cnt;
}
}
void tarjan()
{
memset(dfn,,sizeof dfn);cnt=;tot=;
for(int i=;i<=n;i++)if(!dfn[i])dfs(i);
}
int main()
{
T=rdn();
while(T--)
{
xnt=;memset(head,,sizeof head);
n=rdn();m=rdn();
for(int i=;i<n;i++)a[i]=rdl();a[n]=INF;int x,y;ll z;
for(int i=;i<=m;i++)
{
x=rdn();y=rdn();z=rdl();add(x,y,z);
}
init();mxflow=;
while(bfs())
{memcpy(cur,head,sizeof head);mxflow+=dinic(,INF);}
tarjan();flag=;
for(int i=,u,v;i<=xnt;i+=)//只看非反向边
if(!ed[i].cap&&col[u=ed[i^].to]!=col[v=ed[i].to])//
if(a[u]==a[v]||col[u]!=col[]||col[v]!=col[n]){flag=;break;}
if(flag)printf("No %lld\n",mxflow);else printf("Yes %lld\n",mxflow);
}
return ;
}

bzoj3258秘密任务的更多相关文章

  1. BZOJ3258: 秘密任务

    题解: 其实就是一个简单的最小割判断是否唯一解... 可是我写了一上午还没过...T_T 把1-n的最短路上的边提出来做最小割. 然后从s,t分别bfs判断必须在某个割的点.如果有的点没有被bfs到, ...

  2. TypeScript: Angular 2 的秘密武器(译)

    本文整理自Dan Wahlin在ng-conf上的talk.原视频地址: https://www.youtube.com/watch?v=e3djIqAGqZo 开场白 开场白主要分为三部分: 感谢了 ...

  3. [C#] string 与 String,大 S 与小 S 之间没有什么不可言说的秘密

    string 与 String,大 S 与小 S 之间没有什么不可言说的秘密 目录 小写 string 与大写 String 声明与初始化 string string 的不可变性 正则 string ...

  4. 网站的SEO以及它和站长工具的之间秘密

    博客迁移没有注意 URL 地址的变化,导致百度和 google 这两只爬虫引擎短时间内找不到路.近段时间研究了下国内最大搜索引擎百度和国际最大搜索引擎google的站长工具,说下感受. 百度的站长工具 ...

  5. 《WePayUI组件设计的秘密》——2016年第一届前端体验大会分享

    本文是博主参加第一届前端体验大会 | 物勒工名做的分享<WePayUI组件设计的秘密>,内容主要分为2个部分: 一.浅析UI库/框架的未来 讨论的UI库或者框架,主要包含展示和交互的css ...

  6. [从产品角度学EXCEL 03]-单元格的秘密

    这是<从产品角度学EXCEL>系列——单元格的秘密. 前言请看: 0 为什么要关注EXCEL的本质 1 EXCEL是怎样运作的 2 EXCEL里的树形结构 或者你可以去微信公众号@尾巴说数 ...

  7. 2016第16本:TED演讲的秘密

    花0.01元抢购了<得到APP>中的<成甲说书:TED演讲的秘密>,不到30分钟的音频,感觉全是干货,基本不用看原书了.如果在以后的演讲中随便应用几条都可以让演讲水平提升一大截 ...

  8. 字符串的replace()方法隐藏着什么不可告人秘密?

    最近在做JS算法项目时发现一个令我匪夷所思的问题, 这里想记录一下问题. 首先介绍一下字符串replace()方法的基本用法. replace() 方法使用一个替换值(replacement)替换掉一 ...

  9. 第一章-第七题( 有人认为,“中文编程”, 是解决中国程序员编程效率一个秘密武器,请问它是一个 “银弹” 么? )--By 侯伟婷

    首先,“银弹”在百度百科中的解释是银色的子弹,我们更熟知的“银弹”一词,应该是在<人月神话>中提到的.银弹原本应该是指某种策略.技术或者技巧可以极大地提高程序员的生产力[1].此题目中关于 ...

随机推荐

  1. 二十二 Python分布式爬虫打造搜索引擎Scrapy精讲—scrapy模拟登陆和知乎倒立文字验证码识别

    第一步.首先下载,大神者也的倒立文字验证码识别程序 下载地址:https://github.com/muchrooms/zheye 注意:此程序依赖以下模块包 Keras==2.0.1 Pillow= ...

  2. C#/JAVA 程序员转GO/GOLANG程序员笔记大全(DAY 06)

    ----------------------------------------- go 并发 // 注解:go 语言天生为程序并发所设计,可以说go的强项就是在cpu并发上的处理. // go 语言 ...

  3. JavaScript---js的模块化

    js的模块模式被定义为给类提供私有和公共封装的一种方法,也就是我们常说的“模块化”. 怎么实现“模块化”? 通过闭包的原理来实现“模块化”  ,具体实现:1.必须有外部的封闭函数,该函数必须至少被调用 ...

  4. log4j打印不同颜色

    1.首先在eclipse中安装一个插件: ANSI COLOR 在Eclipse Marketplace 中直接搜索  ANSI COLOR 然后安装 2.在log4j 中加入红色字体部分: < ...

  5. certbot以standalone方式新建密钥

    下载:wget https://dl.eff.org/certbot-auto 授权:chmod a+x ./certbot-auto 快捷命令 ./certbot-auto certonly --t ...

  6. losetup命令

    Linux losetup命令用于设置循环设备. 循环设备可把文件虚拟成区块设备,籍以模拟整个文件系统,让用户得以将其视为硬盘驱动器,光驱或软驱等设备,并挂入当作目录来使用. 语法 losetup [ ...

  7. vue 初级小总结

    (1)插值,即渲染文本 文本:数据绑定最常见的形式就是使用 {{...}}(双大括号)的文本插值 <h1>{{ message }}</h1> data() { return ...

  8. Unity内存优化技术测试案例

    笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D实战核心技术详解 ...

  9. SpreadJS 在 Angular2 中支持绑定哪些属性?

    SpreadJS 纯前端表格控件是基于 HTML5 的 JavaScript 电子表格和网格功能控件,提供了完备的公式引擎.排序.过滤.输入控件.数据可视化.Excel 导入/导出等功能,适用于 .N ...

  10. vue样式穿透

    在一次这样的需求中,需要实现滑倒底部时自动请求数据,需要动态创建节点然后追加到某元素中,这期间遇到的问题就是在动态创建节点后,类名也已经加上了 ,但是样式就是没有生效,最后发现原因的产生竟然是< ...