zoukankan      html  css  js  c++  java
  • 矩阵树定理+二项式反演复习

    矩阵树定理+二项式反演复习

    = 计数题复习

    二项式反演

    二项式定理的至多和恰好的转化:

    [f_n = sum_{i=0}^n {nchoose i} g_i Leftrightarrow g_n=sum_{i=0}^n (-1)^{n-i}{nchoose i} f_i ]

    至少和恰好的转化:

    [f_n= sum_{i=n}^m {ichoose n}g_iLeftrightarrow g_n=sum_{i=n}^m(-1)^{i-n}{ichoose n} f_i ]

    这两个是比较有用的,一般看到“恰好"就要直接想到二项式反演。

    然后min-max容斥也可以通过二项式反演直接推出来

    [max(S)=sum_{Tsubset S}(-1)^{|T|-1}min(T) ]

    BZOJ2839

    裸题,之前学的时候没做

    恰好转化成至少就好了

    PKUWC2018 随机游走

    考虑minmax容斥之后怎么做,有dp式子:

    [f(S,i)=[i otin S]left(sum_v frac{f(S,v)}{deg_i}+1 ight) ]

    高消好像有点慢。下面是抄题解时间。(大师,我悟了!)

    (f[i]=A[i]*f[fa]+B[i]) ,尝试推出f的递推式:

    [egin{aligned} f[i] &=frac{1}{operatorname{deg}[i]}left(f[f a]+sum_{j in s o n_{i}} f[j] ight)+1 \ f[i] &=frac{1}{operatorname{deg}[i]}left(f[f a]+sum_{j in s o n_{i}}(A[j] imes f[i]+B[j]) ight)+1 \ left(1-frac{sum_{j in s o n_{i}} A[j]}{operatorname{deg}_{i}} ight) f[i] &=frac{1}{operatorname{deg}[i]} f[f a]+frac{1}{operatorname{deg}[i]} sum_{j in s o n_{i}} B[j]+1 \ left(operatorname{deg}[i]-sum_{j in s o n_{i}} A[j] ight) f[i] &=f[f a]+sum_{j in s o n_{i}} B[j]+operatorname{deg}[i] \ f[i] &=frac{1}{left(operatorname{deg}[i]-sum_{j in operatorname{son}_{i}} A[j] ight)} f[f a]+frac{sum_{j in operatorname{son}_{i}} B[j]+operatorname{deg}[i]}{left(operatorname{deg}[i]-sum_{j in operatorname{son}_{i}} A[j] ight)} end{aligned} ]

    那么就可以(n2^n)递推了,最后FWT搞出高维前缀和。(minmax容斥多次询问一般配合高维前缀和食用QwQ)总复杂度(O(n2^nlogw+nq))

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define ll long long
    #define FOR(i,a,b) for(register int i=a;i<=b;++i) 
    #define ROF(i,a,b) for(register int i=a;i>=b;--i)
    const int mod = 998244353;
    #define chk(a,b) a=(a+b)>=mod?a+b-mod:a+b;  
    using namespace std;
    int read(){
    	int x=0,pos=1;char ch=getchar();
    	for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
    	for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
    	return pos?x:-x;
    } 
    const int N = 1<<19;
    struct node{
    	int v,nex;
    }edge[100];
    int head[1001],top=0;
    void add(int u,int v){
    	edge[++top].v=v;edge[top].nex=head[u];head[u]=top;
    }
    int n,q,x;
    int ksm(int a,int b){int res=1;while(b){if(b&1) res=1ll*res*a%mod;a=1ll*a*a%mod,b>>=1;}return res;}
    int f[N],a[20],b[20];
    short sum[N];
    void dfs(int now,int fa,int S){
    	if((1<<(now-1))&S) return;
    	int dg=0;
    	for(int i=head[now];i;i=edge[i].nex){
    		int v=edge[i].v;dg++;
    		if(v==fa) continue;
    		dfs(v,now,S);
    		(a[now]+=a[v])%=mod,(b[now]+=b[v])%=mod;
    	}
    	a[now]=(dg-a[now]+mod)%mod;b[now]=(b[now]+dg)%mod;
    	a[now]=ksm(a[now],mod-2);b[now]=1ll*b[now]*a[now]%mod; 
    }
    void fwt(int *f){
    	for(int l=2;l<=(1<<n);l*=2){ int m=l/2;
    		for(int *g=f;g!=f+(1<<n);g+=l){
    			for(int j=0;j<m;j++){
    				g[j+m]=(g[j+m]+g[j])%mod;
    			}
    		}
    	}
    }
    int main(){
    	n=read(),q=read(),x=read();
    	for(int i=1;i<n;i++){
    		int u=read(),v=read();
    		add(u,v);add(v,u);
    	}
    	for(int i=1;i<(1<<n);i++){
    		memset(a,0,sizeof a),memset(b,0,sizeof b);
    		dfs(x,0,i);
    		sum[i]=sum[i>>1]+(i&1);
    		if(!(sum[i]&1)) f[i]=(mod-b[x])%mod;else f[i]=b[x];
    	}
    	fwt(f);
    	for(int i=1;i<=q;i++){
    		int k=read(),now=0;
    		for(int j=1;j<=k;j++){
    			int u=read();now|=(1<<(u-1));
    		}
    		printf("%d
    ",f[now]);
    	}
    	return 0;
    }
    

    SDOI2014 重建

    [egin{align}ans &= sum_{Tsubset S} prod_{ein T} P_eprod_{e ot in T}(1-P_e)\&= sum_{Tsubset S}prod_{ein T} P_efrac{prod _e (1-P_e)}{prod_{ein T}(1-P_e)}\&=prod_{e} (1-P_e)sum_{Tsubset S}prod _{ein T}frac{P_e}{1-P_e}end{align} ]

    然后有个东西叫做变元矩阵树定理(第一次听说

    矩阵树定理求的是(sum_{Tsubset S}prod_{ein T} 1),考虑矩阵的构造:

    [oldsymbol{L}_{i, j}=left{egin{array}{l}sum_{j=1}^{|V|}[(i, j) in E], i=j(i ext {的度数}) \-sum_{e in E}[(i, j)=e], i eq j( ext {边} i, j ext {的个数})end{array} ight. ]

    (L_{i,i})换成与i相邻的边的权值和,(L_{i,j})换成ij权值的负值,跟矩阵树定理差不多,就可以直接做了

    #include<bits/stdc++.h>
    #define db long double
    using namespace std;
    int read(){
    	int x=0,pos=1;char ch=getchar();for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
    	for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';return pos?x:-x;
    }
    #define FOR(i,a,b) for(int i=a;i<=b;++i)
    const int N = 201;
    #define ROF(i,a,b) for(int i=b;i>=a;--i)
    int n; 
    db g[N][N],f[N][N];
    const db eps = 1e-7;
    void gauss(){
    	for(int i=1;i<=n;i++){
    		int x=i;
    		FOR(j,i+1,n){
    			if(fabs(f[x][i])<fabs(f[j][i])) x=j;
    		}
    		swap(f[x],f[i]);
    		FOR(j,i+1,n){
    			db d=f[j][i]/f[i][i];
    			ROF(k,i,n){
    				f[j][k]-=d*f[i][k];
    			}
    		}
    	}
    }
    int main(){
    	n=read();
    	db ans=1;
    	FOR(i,1,n){
    		FOR(j,1,n){
    			scanf("%Lf",&g[i][j]);
    			if(g[i][j]==0) g[i][j]=eps;
    			if(g[i][j]==1) g[i][j]-=eps;
    			if(i<j) ans*=(1-g[i][j]);
    		}
    	}
    	FOR(i,1,n){
    		FOR(j,1,n){
    			if(i==j){
    				FOR(l,1,n){
    					if(i!=l) f[i][i]+=g[i][l]/(1-g[i][l]);
    				}
    			}else{
    				f[i][j]=-g[i][j]/(1-g[i][j]);
    			}
    		}
    	}
    	--n;
    	gauss();
    	for(int i=1;i<=n;i++){
    		ans*=f[i][i];
    	}
    	printf("%.20Lf",ans);
    	return 0;
    }
    

    普通的矩阵树定理(标题:复习)

    f(i,j) = i和j是否有边*-1

    f(i,i) = i的度数

    然后去掉一行一列(一般去最后),求det就是答案(消成上三角对角线乘起来)

    就先这么多吧

  • 相关阅读:
    CentOS怎样强制卸载PHP以及自定义安装PHP
    HTMLparser 笔记
    linux如何查看CPU,内存,机器型号,网卡信息
    PC机做ISCSI存储服务器故障
    [ Python
    [ Python
    [ Python
    [ Python
    [ Python
    [ Python
  • 原文地址:https://www.cnblogs.com/lcyfrog/p/12903195.html
Copyright © 2011-2022 走看看