zoukankan      html  css  js  c++  java
  • 洛谷 P5406

    题面传送门

    首先看到这道题你必须要有一个很清楚的认识:这题新定义的 (oplus) 符号非常奇怪,也没有什么性质而言,因此无法通过解决最优化问题的思路来解决这个问题,只好按照计数题的思路来解决,即考虑 (cnt_k) 表示权值为 (k) 的生成树的个数,那么我们即求最大的满足 (cnt_k e 0)(k)

    考虑怎样维护这个东西,首先考虑一个非常 trivial 的情况,如果题目中求生成树权值的方法不是这个奇怪的 (oplus) 运算而是加法怎么办。那么对于一条权值为 (w) 的边,如果它在生成树中,它会对生成树权值产生 (w) 的贡献,因此很自然地想到用幂级数表示权值,即将每条边看作一个幂级数 (x^w),然后跑矩阵树定理会得出一个幂级数 (F(x)),那么权值为 (v) 的生成树棵数就是 ([x^v]F(x))

    接下来考虑将加法换为二进制运算后怎样处理,我们还是将每条边看作一个幂级数 (x^w),那么与之前唯一的区别是,原来幂级数 (x^u)(x^v) 乘积的贡献会累加到 (x^{u+v}) 上,即加法卷积,现在 (x^u)(x^v) 的乘积则会累加到 (x^{u|v})(或者 (x^{u&v}),或 (x^{uoperatorname{xor}v}))上,即二进制卷积。看到二进制卷积我们很自然地想到一个东西——FWT,因此如果题目中的 (oplus) 只是 (operatorname{and})(operatorname{or})(operatorname{xor}) 的话,那么我们就可以一遍 FWT,再求出基尔霍夫矩阵解出每种权值的行列式,再 IFWT 回去求出所有的 (cnt_k)。而现在对于新定义的 (oplus) 运算,虽然它每一位的运算规律都不同,但由于它们彼此之间互相独立,并且都是二进制运算,因此 FWT 对于该运算依然适用,具体来说对于每一位如果它是 ^ 就按照 FWTxor/IFWTxor 的套路合并,&,| 也同理,这样即可求出最终的 (cnt_k)

    时间复杂度 (2^wn^3),可以通过此题。记得模上一个大质数,为了防止冲突我取了 (993244853)(请求出生成树个数模 (993244853) 等于 (0) 的概率(大雾))。

    const int MOD=993244853;
    const int INV2=496622427;
    const int MAXN=70;
    const int MAXV=1<<12;
    int qpow(int x,int e){
    	int ret=1;
    	for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
    	return ret;
    }
    void add(int &x,int v){((x+=v)>=MOD)&&(x-=MOD);}
    int n,m,k;char s[14];
    int a[MAXN+5][MAXN+5][MAXV+5],b[MAXV+5];
    void FWT(int *a,int type){
    	for(int i=2,lg=0;lg<k;i<<=1,++lg){
    		for(int j=0;j<(1<<k);j+=i)
    			for(int l=0;l<(i>>1);l++){
    				if(s[lg]=='|'){
    					if(~type) add(a[(i>>1)+j+l],a[j+l]);
    					else add(a[(i>>1)+j+l],MOD-a[j+l]);
    				} else if(s[lg]=='&'){
    					if(~type) add(a[j+l],a[(i>>1)+j+l]);
    					else add(a[j+l],MOD-a[(i>>1)+j+l]);
    				} else {
    					int X=a[j+l],Y=a[(i>>1)+j+l];
    					if(~type) a[j+l]=(X+Y)%MOD,a[(i>>1)+j+l]=(X-Y+MOD)%MOD;
    					else a[j+l]=1ll*INV2*(X+Y)%MOD,a[(i>>1)+j+l]=1ll*INV2*(X-Y+MOD)%MOD;
    				}
    			}
    	}
    }
    int getdet(int x){
    	int sgn=1;
    	for(int i=1;i<n;i++){
    		int t=i;
    		for(int j=i+1;j<n;j++) if(a[j][i][x]) t=j;
    		for(int j=i;j<n;j++) swap(a[i][j][x],a[t][j][x]);
    		if(t^i) sgn=-sgn;
    		int iv=qpow(a[i][i][x],MOD-2);
    		for(int j=i+1;j<n;j++){
    			int mul=1ll*(MOD-iv)*a[j][i][x]%MOD;
    			for(int l=i;l<n;l++) a[j][l][x]=(a[j][l][x]+1ll*mul*a[i][l][x])%MOD;
    		}
    	} int res=(sgn+MOD)%MOD;
    //	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) printf("%d%c",a[i][j][x]," 
    "[j==n]);
    	for(int i=1;i<n;i++) res=1ll*res*a[i][i][x]%MOD;
    	return res;
    }
    int main(){
    	scanf("%d%d%s",&n,&m,s);k=strlen(s);
    	for(int i=1,u,v,w;i<=m;i++){
    		scanf("%d%d%d",&u,&v,&w);--u;--v;
    		add(a[u][v][w],MOD-1);add(a[v][u][w],MOD-1);
    		add(a[u][u][w],1);add(a[v][v][w],1);
    	}
    	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) FWT(a[i][j],1);
    //	for(int i=0;i<(1<<k);i++){
    //		for(int u=1;u<n;u++) for(int v=1;v<n;v++)
    //			printf("%d%c",a[u][v][i]," 
    "[v==n]);
    //		printf("
    ");
    //	}
    	for(int i=0;i<(1<<k);i++) b[i]=getdet(i)/*,printf("%d
    ",b[i])*/;
    	FWT(b,-1);for(int i=(1<<k)-1;~i;i--) if(b[i]) return printf("%d
    ",i),0;
    //	assert(114514^1919810^114514^1919810);
    	puts("-1"); 
    	return 0;
    }
    
  • 相关阅读:
    Selenium系列(十五)
    Selenium系列(十四)
    Selenium系列(十三)
    Selenium系列(十二)
    Linux常用命令
    Linux
    Linux常用命令
    Linux常用命令
    Mysql常用sql语句(2)- 操作数据表
    Linux常用命令
  • 原文地址:https://www.cnblogs.com/ET2006/p/luogu-P5406.html
Copyright © 2011-2022 走看看