[HNOI2013]消毒
作者:互联网
题目
思路
哪里买的这种
F
\sout{\;F\;}
F试剂,请务必给我来一打。
如果
min
(
x
,
y
,
z
)
=
x
\min(x,y,z)=x
min(x,y,z)=x 的话,干脆把
y
,
z
y,z
y,z 设置为
+
∞
+\infty
+∞ 好了!那么,只要
x
x
x 被包含在了范围内,一切污垢都将被除尽!(正道的光
♪
\sout{\;♪\;}
♪,照在了大地上……)
而此时,完全可以令 x = 1 x=1 x=1 但操作很多次。毕竟操作次数不重要,重要的是 F F F 试剂的使用量。
所以说,一个需要被消毒的点 ( x 0 , y 0 , z 0 ) (x_0,y_0,z_0) (x0,y0,z0) 只有三种选择:消灭所有 x = x 0 x=x_0 x=x0 的点;消灭所有 y = y 0 y=y_0 y=y0 的点;消灭所有 z = z 0 z=z_0 z=z0 的点。
考虑到 1 8 3 = 5 , 832 18^3=5,832 183=5,832 ,可以知道 min ( a , b , c ) ≤ 17 \min(a,b,c)\le 17 min(a,b,c)≤17 ,可以暴力枚举。此时只剩了两个选项。无非就是:不选 x x x 则必须选 y y y 。这不是 最大权闭合子图 吗?不懂就看别人的题解。
然后你发现这玩意儿就是二分图匹配嘛——物品的代价都是 ± 1 \pm 1 ±1 。所以 d i n i c \tt dinic dinic 复杂度有保证!
建边不可能每次都重头建。考虑边 ⟨ y 0 , z 0 ⟩ \lang y_0,z_0\rang ⟨y0,z0⟩ 不存在的条件: ∀ ⟨ x , y 0 , z 0 ⟩ \forall \langle x,y_0,z_0\rangle ∀⟨x,y0,z0⟩ 都满足 x x x 被处理了。所以状压就可以在 B B B 和 C C C 之间只建一条边。
代码
由于复杂度是玄学,必须开 − O 2 -O2 −O2 才能过。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long int_;
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
const int MaxN = 10005;
struct Edge{
int to, nxt, val, need;
Edge(){ /* nothing */; }
Edge(int T,int N,int V,int NE){
to = T, nxt = N, val = V;
need = NE;
}
};
Edge e[MaxN<<2];
int head[MaxN], cntEdge;
void addEdge(int a,int b,int c,int d){
e[cntEdge] = Edge(b,head[a],c,d);
head[a] = cntEdge ++;
e[cntEdge] = Edge(a,head[b],0,d);
head[b] = cntEdge ++;
}
int nowStatus; // enumerated status
queue< int > q; int dis[MaxN];
bool bfs(int s,int t,int n){
while(!q.empty()) q.pop();
memset(dis,-1,n<<2);
q.push(s), dis[s] = 0;
while(!q.empty()){
s = q.front(); q.pop();
for(int i=head[s]; ~i; i=e[i].nxt)
if(e[i].val && !(~dis[e[i].to])
&& (nowStatus&e[i].need) != e[i].need){
dis[e[i].to] = dis[s]+1;
q.push(e[i].to);
}
// printf("dis[%d] = %d\n",s,dis[s]);
}
return dis[t] != -1;
}
int cur[MaxN]; // current edge optimization
int dfs(int x,int inFlow,const int &t){
int sum = 0; if(x == t) return inFlow;
for(int &i=cur[x]; ~i; i=e[i].nxt)
if(dis[e[i].to] == dis[x]+1 && e[i].val
&& (nowStatus&e[i].need) != e[i].need){
int d = min(inFlow-sum,e[i].val);
// printf("%d -> %d with %d\n",x,e[i].to,d);
d = dfs(e[i].to,d,t);
e[i].val -= d, e[i^1].val += d;
if((sum += d) == inFlow) break;
}
if(!sum) dis[x] = -1; // eat s**t
return sum;
}
const int infty = (1<<30)-1;
int dinic(int s,int t,int n){
int res = 0;
while(bfs(s,t,n)){
memcpy(cur,head,n<<2);
res += dfs(s,infty,t);
}
return res;
}
int ***maze, A, B, C;
void input(){
static int len[3], id[3];
len[0] = A = readint();
len[1] = B = readint();
len[2] = C = readint();
id[0] = 0, id[1] = 1, id[2] = 2;
if(A < B) swap(A,B), swap(id[0],id[1]);
if(A < C) swap(A,C), swap(id[0],id[2]);
if(B < C) swap(B,C), swap(id[1],id[2]);
maze = new int**[A];
for(int i=0; i<A; ++i)
maze[i] = new int*[B];
for(int i=0; i<A; ++i)
for(int j=0; j<B; ++j)
maze[i][j] = new int[C];
static int i[3]; // variable
for(i[0]=0; i[0]<len[0]; ++i[0])
for(i[1]=0; i[1]<len[1]; ++i[1])
for(i[2]=0; i[2]<len[2]; ++i[2])
maze[i[id[0]]][i[id[1]]][i[id[2]]] = readint();
}
int bitcnt[1<<17];
void solve(){
memset(head,-1,MaxN<<2);
cntEdge = 0; // clear graph
for(int i=0; i<A; ++i)
for(int j=0; j<B; ++j){
int S = 0; // {i}
for(int k=0; k<C; ++k)
if(maze[i][j][k] == 1)
S |= (1<<k);
// printf("%d %d with %d\n",j,k+B,S);
if(S) addEdge(i,j+A,infty,S);
}
for(int i=0; i<A; ++i)
addEdge(A+B,i,1,1<<C);
for(int j=0; j<B; ++j)
addEdge(j+A,A+B+1,1,1<<C);
int ans = A+B+C; // real ans
for(nowStatus=0; nowStatus<(1<<C); ++nowStatus){
// B-dinic = saving from B
int xez = dinic(A+B,A+B+1,A+B+2);
ans = min(ans,bitcnt[nowStatus]+xez);
/* Maintain edges */ ;
for(int i=head[A+B]; ~i; i=e[i].nxt)
e[i].val = 1, e[i^1].val = 0;
for(int i=head[A+B+1]; ~i; i=e[i].nxt)
e[i].val = 0, e[i^1].val = 1;
for(int j=0; j<A; ++j)
for(int i=head[j]; ~i; i=e[i].nxt)
if(e[i].to != A+B){
e[i].val = infty;
e[i^1].val = 0;
}
// printf("nowStatus = %d, dinic = %d\n",nowStatus,xez);
}
printf("%d\n",ans);
}
int main(){
bitcnt[0] = 0;
for(int i=1; i<(1<<17); ++i)
bitcnt[i] = bitcnt[i^(i&-i)]+1;
int T = readint();
while(T --){
input(), solve();
delete maze; // save some memory
}
return 0;
}
标签:head,val,min,消毒,HNOI2013,int,id,dis 来源: https://blog.csdn.net/qq_42101694/article/details/113695823