zoukankan      html  css  js  c++  java
  • [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/
  • 相关阅读:
    《Programming WPF》翻译 第8章 1.动画基础
    一些被遗忘的设计模式
    《Programming WPF》翻译 第4章 数据绑定
    《Programming WPF》翻译 第3章 控件
    《Programming WPF》翻译 第5章 样式和控件模板
    《Programming WPF》翻译 第7章 绘图
    《Programming WPF》翻译 第9章 自定义控件
    《Programming WPF》翻译 第7章 绘图 (2)
    《Programming WPF》翻译 第8章 前言
    关于Debug和Release之本质区别
  • 原文地址:https://www.cnblogs.com/Menteur-Hxy/p/9640627.html
Copyright © 2011-2022 走看看