其他分享
首页 > 其他分享> > [HNOI2013]消毒

[HNOI2013]消毒

作者:互联网

题目

传送门 to luogu

思路

哪里买的这种    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