Codeforces 题目传送门 & 洛谷题目传送门

首先暴力显然是不行的,如果你暴力最大流过了我请你吃糖

注意到本题的 \(k\) 很小,考虑以此为突破口解题。根据最大流等于最小割定理,点 \(1\) 到点 \(n\) 的最大流,等于边权和最小的边集 \(E\) 的边权和,满足割掉 \(E\) 中的边后 \(1\) 与 \(n\) 不连通(这里稍微多说几句,关于最大流最小割的“割”,最原始的定义是将点集 \(V\) 分成两个点集 \(V_1,V_2\),满足 \(V_1\cup V_2=V\),并且 \(S\in V_1,T\in V_2\),并且定义了割的权值 \(\sum\limits_{u\in V_1,v\in V_2}c(u,v)\),最上面的“割掉 \(E\) 的边后 \(1\) 与 \(n\) 不连通”有本质区别,上述结论只是它的一个推论)。我们不妨枚举 \(E\) 与特殊边集合的并集 \(S\),也就是说对于 \(S\) 中的特殊边我们强制要求它必须被割掉,对于不在 \(S\) 中的特殊边我们强制要求它不能被割掉。显然这样的 \(S\) 总共有 \(2^k\) 个,因此我们可以对每个 \(S\) 计算答案并更新 \(ans\)。

那么怎么计算集合 \(S\) 的答案呢?首先集合 \(S\) 中的边的权值是肯定要累加入答案中的,因此我们首先求出 \(\sum\limits_{e\in S}w_e\),这个显然可以通过类似于前缀和的东西以单组询问 \(2^k\) 的复杂度预处理出来。接下来考虑怎么计算不在 \(S\) 中的边的贡献,我们建一张新图,包含所有非特殊边和所有不在 \(S\) 中的特殊边,其中非特殊边的容量就是其本身的容量,特殊边的容量为 \(\infty\),因为它不能被割掉,当然由于 \(w\le 25\) 你也可以将其设为 \(25\)。至于 \(S\) 中的特殊边……既然它已经被鸽割掉了就不用管它了。我们只需在这张图上跑一遍最小割即最大流即可。

不过问题又来了,根据常识点数边数 \(10^4\) 级别的图跑最大流复杂度大约是 \(10^7\) 级别的,而我们对 \(2^k\) 个集合每个都跑一遍最大流,复杂度高达 \(10^{10}\),无法通过。不过注意到这 \(2^k\) 张图都是在原本非特殊边构成的图的基础上,添加 \(\le k\) 条容量为 \(25\) 的特殊边构成的,因此我们考虑先对非特殊边跑一遍最大流求出它的残余网络,然后对所有集合 \(S\) 在残余网络上加边跑出新增的流量。注意到这题边的容量很小,因此 Dinic 复杂度甚至不如最朴素的 FF 算法。具体来说,我们随便找一条 \(1\to n\) 的增广路径并更新流量,可以证明这样做的复杂度是 \(\mathcal O(wm)\),因此总复杂度就降到了 \(2^k·wm+q2^k\)。

这是蒟蒻第一次写 FF 哦,早安,Dinic 人

还有就是此题非常卡常,既卡 TLE 又卡 MLE,我大约交了十几发才通过,这里给出几个小卡常技巧:

  • 由于此题边数 \(\le 2\times 10^4<2^{16}\),因此数组可以开成 short,这样空间常数可小一半。
  • 在 FF 的过程中,如果发现已经 BFS 到了 \(n\) 就 break 掉,及时停止,也算是一个小小的剪枝吧。
  • 我是暴力开 \(2^{10}\) 个图的,我们记 \(G_S\) 为加入 \(S\) 中的边后跑完最大流后的残余网络,在计算 \(G_S\) 的最大流时,你不必以 \(G_0\) 为基础加入 \(|S|\) 条边,可以用 lowbit 找到 \(S\) 中最小的元素并以 \(G_{S-\{x\}}\) 为基础,这样只用加入一条边,常数会小不少。
  • 记得写读入优化,这题读入量有点大。

然鹅还是跑得很慢啊……最慢的点跑了 4.5s,几乎是卡时限过题,不保证下次提交依然可以通过。

namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
using namespace fastio;
void chkmin(int &x,int y){(y-x>>31)&&(x=y);}
const int MAXN=1e4;
const int MAXM=2e4;
const int MAXK=10;
const int MAXMSK=1<<10;
const int INF=0x3f3f3f3f;
int n,m,k,qu,sum[MAXMSK+5];
int su[MAXK+5],sv[MAXK+5],sw[MAXK+5];
struct graph{
short int hd[MAXN+5],to[MAXM+5],nxt[MAXM+5],cap[MAXM+5],ec=1;
void adde(int u,int v,int w){
to[++ec]=v;cap[ec]=w;nxt[ec]=hd[u];hd[u]=ec;
to[++ec]=u;cap[ec]=0;nxt[ec]=hd[v];hd[v]=ec;
} short int dep[MAXN+5],now[MAXN+5];
bool getdep(){
memset(dep,-1,sizeof(dep));dep[1]=0;
queue<int> q;q.push(1);now[1]=hd[1];
while(!q.empty()){
int x=q.front();q.pop();
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=cap[e];
if(z&&!~dep[y]){
dep[y]=dep[x]+1;
now[y]=hd[y];q.push(y);
}
}
} return ~dep[n];
}
int getflow(int x,int f){
if(x==n) return f;int ret=0;
for(short int &e=now[x];e;e=nxt[e]){
int y=to[e],z=cap[e];
if(dep[y]==dep[x]+1&&z){
int w=getflow(y,min(f-ret,z));
ret+=w;cap[e]-=w;cap[e^1]+=w;
if(ret==f) return ret;
}
} return ret;
}
int dinic(){
int ret=0;
while(getdep()) ret+=getflow(1,INF);
return ret;
}
int ff_bfs(){
memset(dep,-1,sizeof(dep));dep[1]=0;
queue<int> q;q.push(1);
while(!q.empty()){
int x=q.front();q.pop();
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=cap[e];
if(z&&!~dep[y]){
dep[y]=dep[x]+1;now[y]=e;
q.push(y);if(~dep[n]) break;
}
} if(~dep[n]) break;
} if(!~dep[n]) return 0;
int mn=25;
for(int i=n;i^1;i=to[now[i]^1]) chkmin(mn,cap[now[i]]);
for(int i=n;i^1;i=to[now[i]^1]) cap[now[i]]-=mn,cap[now[i]^1]+=mn;
return mn;
}
int ff(){
int delta=0,ret=0;
while(delta=ff_bfs()) ret+=delta;
return ret;
}
} g[MAXMSK+5];
int flw[MAXMSK+5];
int main(){
read(n);read(m);read(k);read(qu);
for(int i=1;i<=k;i++) read(su[i]),read(sv[i]),read(sw[i]);
for(int i=k+1,u,v,w;i<=m;i++) read(u),read(v),read(w),g[0].adde(u,v,w);
flw[0]=g[0].dinic();
for(int i=1;i<(1<<k);i++){
int lwb=i&-i;g[i]=g[i^lwb];
int id=32-__builtin_clz(lwb);
g[i].adde(su[id],sv[id],25);
flw[i]=flw[i^lwb]+g[i].ff();
// printf("%d %d\n",i,flw[i]);
}
// if(n==9743&&qu==200000&&sv[1]==209) cout<<clock()<<endl;
while(qu--){
for(int i=1;i<=k;i++) read(sw[i]);
for(int i=1;i<(1<<k);i++){
int lwb=i&-i,id=32-__builtin_clz(lwb);
sum[i]=sum[i^lwb]+sw[id];
} int ans=INF;
for(int i=0;i<(1<<k);i++){
// printf("%d %d %d\n",i,sum[i],flw[((1<<k)-1)^i]);
chkmin(ans,sum[i]+flw[((1<<k)-1)^i]);
} print(ans);putc('\n');
} print_final();
return 0;
}

Codeforces 1383F - Special Edges(状态压缩+最大流)的更多相关文章

  1. Escape(状态压缩+最大流,好题)

    Escape http://acm.hdu.edu.cn/showproblem.php?pid=3605 Time Limit: 4000/2000 MS (Java/Others)    Memo ...

  2. HDU3605:Escape(状态压缩+最大流)

    Escape Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Subm ...

  3. HDU3605(KB11-M 状态压缩+最大流)

    Escape Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Subm ...

  4. Codeforces 417D Cunning Gena(状态压缩dp)

    题目链接:Codeforces 417D Cunning Gena 题目大意:n个小伙伴.m道题目,每一个监视器b花费,给出n个小伙伴的佣金,所须要的监视器数,以及能够完毕的题目序号. 注意,这里仅仅 ...

  5. HDU 3605 Escape(状态压缩+最大流)

    http://acm.hdu.edu.cn/showproblem.php?pid=3605 题意: 有n个人和m个星球,每个人可以去某些星球和不可以去某些星球,并且每个星球有最大居住人数,判断是否所 ...

  6. Codeforces 1017D The Wu(状态压缩+预处理)

    题意: 给你n m q,表示在这一组数据中所有的01串长度均为n,然后给你一个含有m个元素的multiset,之后有q次询问.每次询问会给你一个01串t和一个给定常数k,让你输出串t和multiset ...

  7. Codeforces C. A Simple Task(状态压缩dp)

    题目描述:  A Simple Task time limit per test 2 seconds memory limit per test 256 megabytes input standar ...

  8. hdu3605(最大流+状态压缩)

    传送门:Escape 题意:给出每个人适合住的星球信息和该星球能住多少人 ,第一行给出n m 代表有 n 个人 m 个星球,然后接下来n行每行m个数字 1代表适合第 i 个星球 0 代表不适合第 i ...

  9. [CodeForces 11D] A Simple Task - 状态压缩入门

    状态压缩/Bitmask 在动态规划问题中,我们会遇到需要记录一个节点是否被占用/是否到达过的情况.而对于一个节点数有多个甚至十几个的问题,开一个巨型的[0/1]数组显然不现实.于是就引入了状态压缩, ...

随机推荐

  1. Java中的函数式编程(三)lambda表达式

    写在前面 lambda表达式是一个匿名函数.在Java 8中,它和函数式接口一起,共同构建了函数式编程的框架.   lambda表达式乍看像是匿名内部类的一种语法糖,但实际上,它们是两种本质不同的事物 ...

  2. Less-(1~4) union select

    Less-1: 核心语句: 无任何防护:回显查询结果或错误内容. 输入单引号闭合语句中的单引号,#注释后面的内容,即可注入.由于有查询结果回显,直接联合注入即可. 1'order by x #(有些环 ...

  3. [no_code][Alpha]事后分析

    $( "#cnblogs_post_body" ).catalog() 设想和目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 我们要解决的 ...

  4. docker run 的基本用法

    docker run 命令用来创建并启动一个容器 语法:docker run [options] image [command] [args-] 示例:docker run -dit -v 别名:容器 ...

  5. 人人都写过的5个Bug!

    大家好,我是良许. 计算机专业的小伙伴,在学校期间一定学过 C 语言.它是众多高级语言的鼻祖,深入学习这门语言会对计算机原理.操作系统.内存管理等等底层相关的知识会有更深入的了解,所以我在直播的时候, ...

  6. 热身训练3 Palindrome

    Palindrome 简要题意:  我们有一个字符串S,字符串的长度不超过500000. 求满足S[i]=S[2n−i]=S[2n+i−2](1≤i≤n)(n≥2)的子串个数.  分析: 我们能通过简 ...

  7. 海思 core 电压动态调整

    http://www.eda365.com/forum.php?mod=viewthread&tid=108620&_dsign=5bee4dcb http://www.eda365. ...

  8. 两个栈实现队列 牛客网 剑指Offer

    两个栈实现队列 牛客网 剑指Offer 题目描述 用两个栈来实现一个队列,完成队列的Push和Pop操作. 队列中的元素为int类型. class Solution: def __init__(sel ...

  9. Codeforces Round #747 (Div. 2)题解

    谢天谢地,还好没掉分,还加了8分,(8分再小也是加啊)前期刚开始有点卡,不过在尽力的调整状态之后,还是顺利的将前面的水题过完了,剩下的E2和F题就过不去了,估计是能力问题,自己还是得认真补题啦. E2 ...

  10. hdu 5056 Boring count (类似单调队列的做法。。)

    给一个由小写字母构成的字符串S,问有多少个子串满足:在这个子串中每个字母的个数都不超过K. 数据范围: 1<=T<= 1001 <= the length of S <= 10 ...