• [51Nod1446] 限制价值树 (容斥+MT定理+折半搜索)


    传送门

    Description

    有N个点(N<=40)标记为0,1,2,...N-1,每个点i有个价值val[i],如果val[i]=-1那么这个点被定义为bad,否则如果val[i] >=0那么这个点为定义为good。现在给这N个点间连上N-1条边,使它们构成一个生成树,定义树中的点为great点当且仅当这个点本身是good点且与其相邻的点中至少有另一个good点。树的价值等于树中所有great点的价值和。定义限制价值树是指价值不大于maxVal的树,问对给定的val[]与maxVal,一共有多少种不同的限制价格树?由于答案太大,可取
    modulo 1,000,000,007后的结果。

    说明:两棵树是不同的,指两棵树的边集不同,注意这里的边都是无向边。

    Input

    多组测试数据,第一行一个整数T,表示测试数据数量,1<=T<=5
    每组测试数据有相同的结构构成:
    每组数据第一行两个整数N与maxVal,满足1<=N<=40,0<=maxVal<=1,000,000,000。
    第二行有N个整数,即val[0] ~ val[N-1]的数值,满足-1<=val[i]<=25,000,000。

    Output

    每组数据一行输出,即限制价格树的个数。

    Sample Input

    3
    4 3
    1 2 -1 3
    4 5
    1 2 -1 3
    5 6
    -1 -1 2 5 5

    Sample Output

    3
    7
    20

    Solution

    f[i]表示至少有i个good点不great的生成树个数(这i个good点只选一种情况)
    g[i]表示恰有i个good点不great的生成树个数(这i个good点只选一种情况)
    以上两个数组可以只用一个数组qwq
    h[i]表示这tot个good点中使i个为great并且权值不超过maxval的方案数
    最终答案就是(sum_{i=1}^{tot}g[i]*h[tot-i])
    f[i]直接由矩阵树定理求出,g[i]显然可以由f[i]递推得到
    h[i]直接折半搜索
    PS:注意不要爆空间

    Code

    //By Menteur_Hxy
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    #define F(i,a,b) for(register int i=(a);i<=(b);i++)
    #define R(i,a,b) for(register int i=(b);i>=(a);i--)
    using namespace std;
    typedef long long LL;
    typedef pair<int,int> PII;
    
    inline LL read() {
    	LL x=0,f=1; char c=getchar();
    	while(!isdigit(c)) {if(c=='-')f=-f;c=getchar();}
    	while(isdigit(c)) x=(x<<1)+(x<<3)+c-48,c=getchar();
    	return x*f;
    }
    
    const int N=41,MOD=1e9+7,MAX=1048580;
    int n,m,tot,cnt,ans;
    int val[N],g[N],h[N],mat[N][N],fac[N],inv[N],Cnt[MAX][21];//注意空间大小
    PII da[MAX];
    
    LL qpow(LL a,LL b) {
    	LL t=1; 
    	while(b) {
    		if(b&1) t=t*a%MOD;
    		a=a*a%MOD; b>>=1;
    	}
    	return t;
    }
    
    void init() {
    	fac[0]=1; F(i,1,40) fac[i]=(LL)fac[i-1]*i%MOD;
    	inv[40]=qpow(fac[40],MOD-2);
    	R(i,0,40-1) inv[i]=(LL)inv[i+1]*(i+1)%MOD;
    }
    
    LL C(int m,int n) {return (LL)fac[m]*inv[m-n]%MOD*inv[n]%MOD;}
    
    void print() {
    	F(i,1,n) {
    		F(j,1,n) cout<<mat[i][j]<<" ";
    		cout<<endl;
    	}cout<<endl;
    }
    
    LL getf(int x) {
    	LL res=1,fla=1;
    	memset(mat,0,sizeof(mat));
    	F(i,1,x) F(j,tot+1,n) mat[i][i]++,mat[j][j]++,mat[i][j]--,mat[j][i]--;
    	F(i,x+1,n) F(j,i+1,n) mat[i][i]++,mat[j][j]++,mat[i][j]--,mat[j][i]--;//注意连边
    	// print();
    	// cout<<x<<endl;
    	F(i,1,n-1) {//注意去掉一行一列!!
    		int mx=i; F(j,i,n-1) if(mat[j][i]!=0) {mx=j;break;} 
    		if(mx!=i) {F(j,i,n-1) swap(mat[i][j],mat[mx][j]);fla=-fla;}
    		if(!mat[mx][i]) return 0;
    		res=(LL)res*mat[i][i]%MOD;
    		LL Inv=qpow(mat[i][i],MOD-2);
    		F(j,i,n-1) mat[i][j]=(LL)mat[i][j]*Inv%MOD;
    		F(j,i+1,n-1) {
    			LL tmp=mat[j][i]; mat[j][i]=0;
    			F(k,i+1,n-1) mat[j][k]=(mat[j][k]-(LL)tmp*mat[i][k]%MOD+MOD)%MOD;
    		}
    		// print();
    	}
    	return res*fla;
    }
    
    void dfs1(int l,int r,int num,int sum) {
    	if(l>r) {da[++cnt]=PII(sum,num);return ;}
    	dfs1(l+1,r,num,sum);
    	if(sum+val[l]<=m) dfs1(l+1,r,num+1,sum+val[l]);
    }
    
    void dfs2(int l,int r,int num,int sum) {
    	if(l>r) {
    		int p=upper_bound(da+1,da+1+cnt,PII(m-sum,n+1))-da-1;//!!!
    		F(i,0,tot/2) h[i+num]=(h[i+num]+Cnt[p][i])%MOD;
    		return ;
    	}
    	dfs2(l+1,r,num,sum);
    	if(sum+val[l]<=m) dfs2(l+1,r,num+1,sum+val[l]);
    }
    
    bool cmp(int x,int y) {return x>y;}
    
    void solve() {	
    	n=read(),m=read();tot=cnt=ans=0;
    	F(i,1,n) val[i]=read(),tot+=(val[i]!=-1);
    	sort(val+1,val+1+n,cmp);
    	F(i,0,tot) g[i]=getf(i);
    	// F(i,0,tot) cout<<g[i]<<" ";cout<<endl;
    	R(i,0,tot) F(j,i+1,tot) g[i]=(g[i]-(LL)C(tot-i,j-i)*g[j]%MOD+MOD)%MOD;
    	dfs1(1,tot/2,0,0);
    	sort(da+1,da+1+cnt);
    	F(i,1,cnt) {
    		F(j,0,tot/2) Cnt[i][j]=Cnt[i-1][j];
    		Cnt[i][da[i].second]++;
    	}
    	memset(h,0,sizeof(h));//初始化!!
    	dfs2(tot/2+1,tot,0,0);
    	F(i,0,tot) ans=(ans+(LL)g[i]*h[tot-i]%MOD)%MOD;
    	// F(i,1,tot) cout<<val[i]<<" ";cout<<endl;cout<<endl;
    	// F(i,1,cnt) cout<<da[i].first<<" "<<da[i].second<<endl;cout<<endl;
    	// F(i,1,cnt) F(j,1,10) if(Cnt[i][j]) cout<<
    	// F(i,0,tot) cout<<g[i]<<" "<<h[i]<<endl;
    	printf("%lld
    ",ans);
    }
    
    int main() {
    	init();
    	int T=read();
    	while(T--) solve();
    	return 0;
    }
    
    版权声明:本文为博主原创文章,未经博主允许不得转载。 博主:https://www.cnblogs.com/Menteur-Hxy/
  • 相关阅读:
    42、lucene和机器学习进行全文搜索,并排序
    41、javaMail机制
    40、dom以xml结尾的文件
    39、重新复习js之三
    38、重新复习javascript之三
    36、重新复习html和css之二
    35、重新复习html与css(1)
    34、Shiro框架入门三,角色管理
    33、插入一段大学学的计算机,正儿八经的计算机图形学
    32、shiro框架入门3.授权
  • 原文地址:https://www.cnblogs.com/Menteur-Hxy/p/9640627.html
Copyright © 2020-2023  润新知