• 洛谷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;
    }
    
  • 相关阅读:
    git删除错误分支
    python学习笔记31:操作ini文件
    小说
    PR去字幕补图实操
    数据备份
    文案
    SQL Server 数据文件收缩和查看收缩进度
    ucorelab15堆栈练习
    前端分页使用js css配合后端Pagehelper做分页功能
    Ubuntu网络设置systemdnetworkd管理网络netplan配置文件
  • 原文地址:https://www.cnblogs.com/Hs-black/p/13408613.html
Copyright © 2020-2023  润新知