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. ORA-19815: WARNING: db_recovery_file_dest_size闪回区爆满问题处理

    问题描述:有一个数据库起不来了,根据层层排查,是因为归档设置在了闪回区,文件的大小已经超出了闪回区限制.最后直接给数据库拖挂 环境:windows server2012 , oracle 19c,单机 ...

  2. 为什么阿里巴巴开发手册中强制要求 POJO 类使用包装类型?NPE问题防范

    封面:学校内的秋天 背景:写这个的原因,也是我这两天凑巧看到的,虽然我一直有 alibaba Java 开发手册,也看过不少次,但是一直没有注意过这个问题 属于那种看过,但又没完全看过 一起来看看吧冲 ...

  3. [no code][scrum meeting] Alpha 1

    项目 内容 会议时间 2020-04-06 会议主题 团队任务分析与拆解 会议时长 30min 参会人员 全体成员 $( "#cnblogs_post_body" ).catalo ...

  4. 软件案例分析——VS、VS Code

    软件案例分析--VS和VS Code 第一部分 调研,测评 一.使用10–30分钟这个软件的基本功能(请上传使用软件的照片) VS code Visual Studio 二.主要功能和目标用户有何不同 ...

  5. logstash收集的日志输出到elasticsearch中

    logstash收集的日志输出到elasticsearch中 一.需求 二.实现步骤 1.编写pipeline文件 1.`elasticsearch`配置参数解析: 2.可能会报的一个异常 2.准备测 ...

  6. ESD

    Reverse standoff voltage是保护二极管的反向工作电压, 在这个电压, 二极管是不工作的. Breakdown voltage 是二极管的击穿电压, 超过这个电压后, 二极管迅速反 ...

  7. [CSP-S2021] 回文

    链接: P7915 题意: 给出一个长度为 \(2n\) 的序列 \(a\),其中 \(1\sim n\) 每个数出现了 2 次.有 L,R 两种操作分别是将 \(a\) 的开头或末尾元素加入到初始为 ...

  8. cf14D Two Paths(树的直径)

    题意: N个点构成一棵树.树枝的长度都是1. 在当中找两条不相交[没有公共点]的路,使得二者长度之积最大. (2 ≤ n ≤ 200) 思路: 一开始思路好麻烦,好麻烦,好麻烦,,,,,,,而且WA, ...

  9. hdu 1166 敌兵布阵(简单线段树or树状数组)

    题意: N个工兵营地,第i个营地有ai个人. 三种操作: 1.第i个营地增加x个人. 2.第i个营地减少x个人. 3.查询第i个到第j个营地的总人数. 思路: 线段树or树状数组 代码:(树状数组) ...

  10. 『学了就忘』Linux基础命令 — 31、grep命令和通配符

    目录 1.grep命令介绍 2.find命令和grep命令的区别(重点) (1)find命令 (2)grep命令 3.通配符与正则表达式的区别 (1)通配符: (2)正则表达式: 1.grep命令介绍 ...