zoukankan      html  css  js  c++  java
  • 【LOJ6622】[THUPC2019] 找树(FWT+矩阵树定理)

    点此看题面

    大致题意: 定义(aoplus b)的第(i)位为(a)的第(i)位与(b)的第(i)位进行给定位运算(oplus_i)(与/或/异或)的结果。给定一张无向图,每条边的边权是一个(w)位二进制数,让你找到一个生成树,求出所有边权进行(oplus)运算的结果的最大值。

    广义(FWT)

    看到这种对于不同位进行不同位运算的题目,我们要想到一个叫做广义(FWT)的东西。

    它其实就是一般(FWT)三种卷积的混合版本,只要在做(FWT)时根据这一位上运算方式的不同采取不同的变换方式就可以了。

    关于它的具体实现可见代码。

    矩阵树定理

    对于生成树,有一个知名算法,就是矩阵树定理

    或许你会感到奇怪,矩阵树定理明明是用于做生成树计数的,而这题求的却是最大值,和计数问题似乎完全没有半点关系啊。

    但实际上,这是一道披着最大化外壳的计数题,而它的转化真的是特别神仙。

    转化

    我们定义(a_{i,j,v})表示仅考虑边权为(v)的边时的基尔霍夫矩阵(i)行第(j)列的值。

    这东西看起来没有任何用处,但如果我们对于每一个数组(a_{i,j}),都做一次广义(FWT),然后就会发现,对于每一个(v),它的基尔霍夫矩阵都独立了!

    于是我们利用高斯消元,求出每一个基尔霍夫矩阵的行列式的值(即生成树个数),存到一个名为(ans)的数组里。

    接着我们再对(ans)(IFWT),则此时的(ans_i)实际上就是做(oplus)运算所得值为(i)的生成树个数!

    那么我们只要找到最大的一个(i)满足(ans_i ot=0),这道题就做完了。

    不得不说,这道题的思路和做法真的都非常妙,是我这种蒟蒻这辈子都想不到的。。。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 70
    #define K 12
    #define X 998244353
    #define I2 499122177
    #define Inc(x,y) ((x+=(y))>=X&&(x-=X))
    #define Dec(x,y) ((x-=(y))<0&&(x+=X))
    #define swap(x,y) (x^=y^=x^=y)
    using namespace std;
    int n,m,w,P,ans[1<<K],A[N+5][N+5],a[N+5][N+5][1<<K];char f[K+5];
    I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
    I void FWT(int *s,CI op)//广义FWT
    {
    	RI t,i,j,k,x,y;for(t=i=1;i^P;++t,i<<=1) for(j=0;j^P;j+=i<<1) for(k=0;k^i;++k) switch(f[t])
    	{
    		case '&':~op?Inc(s[j+k],s[i+j+k]):Dec(s[j+k],s[i+j+k]);break;
    		case '|':~op?Inc(s[i+j+k],s[j+k]):Dec(s[i+j+k],s[j+k]);break;
    		case '^':s[j+k]=((x=s[j+k])+(y=s[i+j+k]))%X,s[i+j+k]=(x-y+X)%X,
    			!~op&&(s[j+k]=1LL*s[j+k]*I2%X,s[i+j+k]=1LL*s[i+j+k]*I2%X);break;
    	}
    }
    I bool Find(CI x)
    {
    	RI i=x+1,j;W(i^n&&!A[i][x]) ++i;if(i==n) return 0;
    	for(j=x;j^n;++j) swap(A[x][j],A[i][j]);return 1;
    }
    I int Det()//高斯消元求行列式的值
    {
    	RI i,j,k,t,g,res=1;for(i=1;i^n;++i)
    	{
    		if(!A[i][i]&&!(res=X-res,Find(i))) return 0;res=1LL*res*A[i][i]%X,g=QP(A[i][i],X-2);
    		for(j=i+1;j^n;++j) for(t=X-1LL*A[j][i]*g%X,k=i+1;k^n;++k) A[j][k]=(1LL*t*A[i][k]+A[j][k])%X;
    	}return res;
    }
    int main()
    {
    	RI i,j,k,x,y,z;scanf("%d%d%s",&n,&m,f+1),P=1<<(w=strlen(f+1));
    	for(i=1;i<=m;++i) scanf("%d%d%d",&x,&y,&z),++a[x][x][z],++a[y][y][z],--a[x][y][z],--a[y][x][z];//初始化基尔霍夫矩阵
    	for(i=1;i<=n;++i) for(j=1;j<=n;++j) {for(k=0;k^P;++k) a[i][j][k]=(a[i][j][k]%X+X)%X;FWT(a[i][j],1);}//FWT
    	for(RI p=0;p^P;++p) {for(i=1;i^n;++i) for(j=1;j^n;++j) A[i][j]=a[i][j][p];ans[p]=Det();}//求出并存下每个矩阵行列式的值
    	for(FWT(ans,-1),i=P-1;~i;--i) if(ans[i]) break;return printf("%d
    ",i),0;//IFWT,然后找到最大答案
    }
    
  • 相关阅读:
    Android随笔
    Android随笔
    Android随笔
    Android随笔
    Android随笔
    Android随笔
    Android随笔
    Android随笔
    Android随笔
    Codeforces Round #551题解
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/LOJ6622.html
Copyright © 2011-2022 走看看