Security Camera [ABC220H]

https://atcoder.jp/contests/abc220/tasks/abc220_h

题解

考虑折半搜索,将 n 个点分为大小为 n2 的两个集合 S,T

F1[s] (sS) 表示如果选了 s 中的点安装摄像头,那么被监视的边的数量的奇偶性

F2[t] (tT) 表示如果选了 t 中的点安装摄像头,那么被监视的两端都在 T的边的数量的奇偶性

G[s] (sS) 表示所有 (和 {S \ s} 中的点之间有奇数条边的) 的 T 中的点的集合

以上都可以在 O(n2n2) 的时间内计算

那么一对 s,t 满足题目条件当且仅当:

F1[s]F2[t] (popcount(G[s]&T)&1)=0

考虑如何计数

枚举 G[s]&T,考虑计算

H[P][0/1]=G[s]&T=P[F1[s]F2[t]=0/1]

Ans=PTH[P][popcount(P)]

发现上面第一个式子长得像个集合交卷积

枚举 v1=0/1,v2=0/1,计算 C1[P]=G[s]=P[F1[s]=v1],C2[P]=[F2[P]=v2]

那么 H[P][v1v2]=s&t=PC1[s]C2[t]

使用 FWTO(n2n2) 的时间内计算

代码

#include <bits/stdc++.h>
#define N 50
#define M (1<<20)+5
#define pb push_back
#define lb(x) (x&-x)
using namespace std;
typedef long long ll;

int n, m, G[M], cnt[M]; 
bool F1[M], F2[M];
ll C1[M], C2[M];
vector<int> E[N];
inline int chk(int s, int x) { return (s>>x)&1; }

void FWT(ll *F, int _n, int tp) {
	for (int i = 1; i < (1<<_n); i <<= 1) {
		for (int j = 0; j < (1<<_n); j += i+i) for (int k = 0; k < i; k++) {
			F[j+k] += tp*F[i+j+k];  
		}
	}
}

int main() {
	scanf("%d %d", &n, &m);
	for (int i = 1, u, v; i <= m; i++) {
		scanf("%d %d", &u, &v); 
		E[u].pb(v); E[v].pb(u);
	}
	int t1 = n/2, t2 = n-t1;
	for (int s = 1; s < (1<<t1); s++) {
		int t = s^lb(s), x = log2(lb(s))+1;
		F1[s] = F1[t]; G[s] = G[t];
		for (auto y : E[x]) {
			if ((y<=t1&&!chk(s,y-1))||y>t1) F1[s]^=1;
			if (y > t1) G[s] ^= (1<<(y-t1-1));
		}
	}
	for (int s = 1; s < (1<<t2); s++) {
		int t = s^lb(s), x = log2(lb(s))+t1+1;
		F2[s] = F2[t]; cnt[s] = cnt[t]+1;
		for (auto y : E[x]) {
			if (y > t1 && !chk(s,y-t1-1)) F2[s]^=1;
		}
	}
	ll ans = 0; int mx = (1<<t1)-1;
	for (int v1 = 0; v1 <= 1; v1++) for (int v2 = 0; v2 <= 1; v2++) {
		memset(C1, 0, sizeof(C1)); memset(C2, 0, sizeof(C2));
		for (int s = 0; s < (1<<t1); s++) if (F1[s] == v1) ++C1[G[mx^s]];
		for (int s = 0; s < (1<<t2); s++) if (F2[s] == v2) ++C2[s];
		FWT(C1, t2, 1); FWT(C2, t2, 1);
		for (int s = 0; s < (1<<t2); s++) C1[s] = C1[s]*C2[s];
		FWT(C1, t2, -1);
		for (int s = 0; s < (1<<t2); s++) {
			if (!((cnt[s]&1)^v1^v2)) ans += C1[s];
		}
	}
	printf("%lld\n", ans);
	return 0;
}
posted @   AK_DREAM  阅读(246)  评论(1编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示