洛谷P5406 [THUPC2019]找树(FWT)

洛谷P5406 [THUPC2019]找树(FWT)

题目大意

定义 (otimes_1, otimes_2, otimes_3) 分别为按位与、按位或、按位异或运算。记 (a_i) 表示 (a) 的从低位到高位的第 (i) 个二进制位。定义一个作用在 (w) 位二进制数上的新运算 (oplus),满足对于结果 (aoplus b) 的每一位 ((aoplus b)_i)((aoplus b)_i = a_i otimes_{large o_i} b_i)。不难验证 (oplus) 运算满足结合律和交换律。

给出一张 (n) 个点 (m) 条边的无向图,每一条边的权值是一个 (w) 位二进制数(即小于 (2^w) 的非负整数)。请你找一棵原图的生成树。设你找出的生成树中的边边权分别为 (v_1,cdots,v_{n-1}),请你最大化 (v_1oplus v_2opluscdotsoplus v_{n-1})

数据范围

接下来 (m) 行,每一行三个非负整数 (x,y,v),表示一条连接 (x)(y) 权值为 (v) 的边,保证 (1leq x,yleq n)(0le v < 2^w)

对于所有数据,(1le nle 70,1le mle 5000,1le w le 12)

来自 THUPC(THU Programming Contest,清华大学程序设计竞赛)2019。

题解等资源可在 https://github.com/wangyurzee7/THUPC2019 查看。

解题思路

大佬做题不证明,蒟蒻看着很难受,因此写一下这篇题解给一些简单的正确性证明

神仙题,首先这题不是最优化题而是数数题,我们算出权值为 k 的生成树个数,如果不为零就可能是答案

生成树计数就看矩阵树定理,矩阵树定理在边权为环的情况下成立,而集合幂级数构成了一个环,加法就是对应相加,乘法就是卷积

先假设所有运算符都是异或

求解行列式我们直接暴力阶乘算法,发现只含有乘法和加法,乘法的时候又是先 FWT 一遍然后点值对应相乘最后再加起来,我们发现每个点值互不影响,所以我们直接分别对每个点值求行列式,这样就可以高斯消元了

现在运算符更加的丰富,你可以将 FWT 看成将 (2^n) 个向量代入求得点值的过程,那么每一位又是独立的,所以我们对应的算用对应的 FWT 即可,看代码就知道具体实现了,如果想要理解为什么是这样的可以看看我这篇文章,或者去翻论文,虽然也不是很好懂(不知道为什么 UOJ 自测调试 1.7s,loj 却要 4s 都不够

#pragma GCC optimize(2)
#include <queue>
#include <vector>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MP make_pair
#define ll long long
#define fi first
#define se second
using namespace std;

template <typename T>
void read(T &x) {
    x = 0; bool f = 0;
    char c = getchar();
    for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
    for (;isdigit(c);c=getchar()) x=x*10+(c^48);
    if (f) x=-x;
}

template<typename F>
inline void write(F x, char ed = '
')
{
	static short st[30];short tp=0;
	if(x<0) putchar('-'),x=-x;
	do st[++tp]=x%10,x/=10; while(x);
	while(tp) putchar('0'|st[tp--]);
	putchar(ed);
}

template <typename T>
inline void Mx(T &x, T y) { x < y && (x = y); }

template <typename T>
inline void Mn(T &x, T y) { x > y && (x = y); }

const int N = 75;
const int Ww = 5048;
const int P = 998244353;
const int inv2 = (P + 1) / 2;
ll e[N][N][Ww], w, n, m, W;
char op[N];
void exFwt(ll *f, int ty) {
	for (int i = 1, t = 1;t <= w; t++, i <<= 1) 
		for (int j = 0;j < W; j += (i << 1)) 
			for (int k = 0;k < i; k++) {
				if (op[t] == '|') f[i+j+k] = (f[i+j+k] + ty * f[j+k] + P) % P;
				else if (op[t] == '&') f[j+k] = (f[j+k] + ty * f[i+j+k] + P) % P;
				else {
					ll x = f[j+k], y = f[i+j+k];
					f[j+k] = (x + y) % P, f[i+j+k] = (x - y + P) % P;
					if (ty == -1) f[j+k] = f[j+k] * inv2 % P,
								f[i+j+k] = f[i+j+k] * inv2 % P;
				}
			}
}

ll M[N][N];

ll fpw(ll x, ll mi) {
	ll res = 1;
	for (; mi; mi >>= 1, x = x * x % P)
		if (mi & 1) res = res * x % P;
	return res;
}

ll Mat(void) {
	ll ans = 1;
	for (int i = 1;i < n; i++) {
		for (int j = i;j < n; j++) {
			if (M[j][i]) {
				if (j == i) break;
				ans = P - ans;
				for (int k = i;k < n; k++) swap(M[i][k], M[j][k]);
				break;
			}
		}
		ans = ans * M[i][i] % P;
		if (!ans) return ans; ll inv = fpw(M[i][i], P - 2);
		for (int j = i;j < n; j++) M[i][j] = M[i][j] * inv % P;
		for (int j = i + 1;j < n; j++) {
			ll t = P - M[j][i];
			for (int k = i;k < n; k++)
				M[j][k] = (M[j][k] + t * M[i][k]) % P;
		}
	}
	return ans;
}

ll f[Ww];
int main() {
	read(n), read(m), scanf ("%s", op + 1);
	w = strlen(op + 1); W = 1 << w;
	for (int i = 1, x, y, v;i <= m; i++) {
		read(x), read(y), read(v);
		e[x][y][v]--, e[y][x][v]--;
		e[x][x][v]++, e[y][y][v]++;
	}
	for (int i = 1;i <= n; i++)
		for (int j = 1;j <= n; j++)
			exFwt(e[i][j], 1);
	for (int i = 0;i < W; i++) {
		for (int j = 1;j <= n; j++) 
			for (int k = 1;k <= n; k++)
				M[j][k] = e[j][k][i];
		f[i] = Mat(); 
	}
	exFwt(f, -1);
	for (int i = W - 1;i >= 0; i--)
		if (f[i] != 0) return write(i), 0;
	return write(-1), 0;
}
原文地址:https://www.cnblogs.com/Hs-black/p/13408613.html