zoukankan      html  css  js  c++  java
  • 洛谷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;
    }
    
  • 相关阅读:
    hdu 2485 Destroying the bus stations 迭代加深搜索
    hdu 2487 Ugly Windows 模拟
    hdu 2492 Ping pong 线段树
    hdu 1059 Dividing 多重背包
    hdu 3315 My Brute 费用流,费用最小且代价最小
    第四天 下载网络图片显示
    第三天 单元测试和数据库操作
    第二天 布局文件
    第一天 安卓简介
    Android 获取存储空间
  • 原文地址:https://www.cnblogs.com/Hs-black/p/13408613.html
Copyright © 2011-2022 走看看