[HNOI2013]消毒

27 篇文章 0 订阅

题目

传送门 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 被包含在了范围内,一切污垢都将被除尽!(正道的光 ♪,照在了大地上……)

而此时,完全可以令 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 的点。

从简单情况开始想:二维情况。此时, x , y x,y x,y 至少要选一个,即 x x x 不选则 y y y 需要选。这就是 2-sat \text{2-sat} 2-sat 嘛。而且是超级简化版:实际上是二分图,求最大独立集(为可选的 0 0 0 的数量),所以 1 1 1 的数量就是最大匹配的值。

第三维呢?考虑到 1 8 3 = 5832 18^3=5832 183=5832,可以知道 min ⁡ ( a , b , c ) ⩽ 17 \min(a,b,c)\leqslant 17 min(a,b,c)17,可以暴力枚举。

d i n i c \tt dinic dinic 跑最大匹配就好了。但不知道为什么,不开 O 2 O2 O2 竟然会全部 TLE \text{TLE} TLE,没道理啊!

代码

#include <cstdio> // ashamed of Queen of Rock Band
#include <iostream> // I'M ILL, INSANE and DESPERATE
#include <algorithm> // inside of Sister, only sister
#include <cstring> // inside of Principal, only principal
#include <cctype> // inside of me, what do you see
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar())
		if(c == '-') f = -f;
	for(; isdigit(c); c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
void writeint(unsigned x){
	if(x > 9) writeint(x/10);
	putchar(char((x%10)^48));
}

const int MAXN = 10005;
struct Edge{
	int to, nxt, val;
	Edge() = default;
	Edge(int _t,int _n,int _v):to(_t),nxt(_n),val(_v){}
};
Edge e[MAXN<<1];
int head[MAXN], cntEdge;
void addEdge(int a,int b,int c){
	e[cntEdge] = Edge(b,head[a],c);
	head[a] = cntEdge ++;
	e[cntEdge] = Edge(a,head[b],0);
	head[b] = cntEdge ++;
}

int que[MAXN], dis[MAXN];
bool bfs(const int S,const int T){
	memset(dis+1,-1,T<<2);
	int *fro = que, *bac = que;
	*(bac ++) = S; dis[S] = 0;
	for(; fro!=bac; ++fro){
		for(int i=head[*fro]; ~i; i=e[i].nxt)
			if(!(~dis[e[i].to]) && e[i].val){
				dis[e[i].to] = dis[*fro]+1;
				*(bac ++) = e[i].to;
			}
	}
	return (~dis[T]);
}
int cur[MAXN];
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){
			int d = dfs(e[i].to,min(inFlow-sum,e[i].val),T);
			e[i].val -= d, e[i^1].val += d;
			if((sum += d) == inFlow) break;
		}
	if(sum != inFlow) dis[x] = -1;
	return sum;
}
const int INF = 0x3fffffff;
int dinic(const int S,const int T){
	int res = 0;
	while(bfs(S,T)){
		memcpy(cur+1,head+1,T<<2);
		res += dfs(S,INF,T);
	}
	return res;
}

int maze[MAXN], bitcnt[1<<17];
int status[MAXN];
int main(){
	for(int i=1; i!=(1<<17); ++i)
		bitcnt[i] = (i&1)+bitcnt[i>>1];
	for(int T=readint(); T; --T){
		int A = readint(), B = readint(), C = readint();
		{ // magic!
			int ys[3] = {0,1,2}; // which is original dimension
			const int len[3] = {A,B,C}; int i[3];
			if(A > B) swap(A,B), swap(ys[0],ys[1]);
			if(A > C) swap(A,C), swap(ys[0],ys[2]);
			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[ys[0]]*B*C+i[ys[1]]*C+i[ys[2]]] = readint();
		}
		for(int j=0; j!=B; ++j) for(int k=0; k!=C; ++k)
			for(int i=status[j*C+k]=0; i!=A; ++i)
				if(maze[i*B*C+j*C+k]) status[j*C+k] ^= (1<<i);
		const int sink = B*C+2; int ans = INF;
		for(int S=0; S!=(1<<A); ++S){
			memset(head+1,-1,sink<<2), cntEdge = 0;
			for(int j=0; j!=B; ++j){
				addEdge(sink-1,j+1,1);
				for(int k=0; k!=C; ++k)
					if((status[j*C+k]&S) != status[j*C+k])
						addEdge(j+1,k+B+1,1);
			}
			for(int k=0; k!=C; ++k) addEdge(k+B+1,sink,1);
			ans = min(ans,bitcnt[S]+dinic(sink-1,sink));
		}
		printf("%d\n",ans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值