限制價值生成樹[1446-51nod][雙搜][矩陣樹]

文章目錄

題目

51nod
在這裏插入圖片描述

思路

太傻了,並不會拆分問題
由於是完全圖,原問題可以拆分成兩個問題

  • 選出 xxgreatgreat 點的方案數,記爲 g(x)g(x)
  • xxgreatgreat 構成合法生成樹的方案數,記爲 f(x)f(x)
    總方案數就是 f(x)g(x)\sum f(x)*g(x)
    假設有 cntcnt 個點不是 1-1
    假設選的 xx 個點爲 1x1\sim x
    那麼剩下的 cntxcnt-x 個點必定和 1-1 相連,並且不會連接前 xx 個點
    建圖就很清晰了
    前面 xx 個點就和 1-1 相連,矩陣樹定理求出方案數記爲 h(x)h(x)
    h(x)h(x) 表示至多有 xxgreatgreat 的方案數
    不難得出 f(x)=h(x)i=0x1C(x,i)f(i)f(x)=h(x)-\sum_{i=0}^{x-1}C(x,i)*f(i)
    g(x)g(x) 用二分搜索即可,合併答案要利用桶
    時間複雜度 O(nn2+1logn+n4)O(n^{\frac{n}{2}+1}logn+n^4)

代碼

#include<set>    
#include<map>    
#include<stack>    
#include<ctime>    
#include<cstdio>    
#include<queue>    
#include<cmath>    
#include<vector>    
#include<cstring>   
#include<climits>    
#include<iostream>   
#include<algorithm>
using namespace std;
#define LL long long
int read(){
    int f=1,x=0;char c=getchar();
    while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();}
    while('0'<=c&&c<='9'){x=x*10+c-'0';c=getchar();}
    return f*x;
}
#define MAXN 40
#define INF 0x3f3f3f3f
#define Mod (int)(1e9+7)
int Add(int x,int y){x+=y;return x>=Mod?x-Mod:x;}
LL a[MAXN+5][MAXN+5],C[MAXN+5][MAXN+5];
LL Pow(LL x,int y){
	LL ret=1;
	while(y){
		if(y&1) ret=ret*x%Mod;
		x=x*x%Mod,y>>=1;
	}
	return ret;
}
LL Det(int n){
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			a[i][j]=(a[i][j]+Mod)%Mod;
	n--;
	LL ret=1;
	for(int i=1;i<=n;i++){
		if(!a[i][i]){
			for(int j=i+1;j<=n;j++)
				if(a[j][i]){
					swap(a[i],a[j]),ret=Mod-ret;
					break;
				}
		}
		if(!a[i][i])
			return 0;
		LL Inv=Pow(a[i][i],Mod-2);
		for(int j=i+1;j<=n;j++){
			LL tmp=a[j][i]*Inv%Mod;
			for(int k=i;k<=n;k++)
				a[j][k]=(a[j][k]-tmp*a[i][k]%Mod+Mod)%Mod;
		}
	}
	for(int i=1;i<=n;i++)
		ret=ret*a[i][i]%Mod;
	return ret;
}
void Prepare(){
	for(int i=0;i<=MAXN;i++)
		C[i][i]=C[i][0]=1;
	for(int i=1;i<=MAXN;i++)
		for(int j=1;j<i;j++)
			C[i][j]=(C[i-1][j]+C[i-1][j-1])%Mod;
	return ;
}
vector<pair<int,int> > p,q;
int bar[MAXN+5],val[MAXN+5],f[MAXN+5],g[MAXN+5];
void DFS(int L,int R,int cnt,int v,vector<pair<int,int> > &x){
	if(L>R){
		x.push_back(make_pair(v,cnt));
		return ;
	}
	DFS(L+1,R,cnt,v,x);
	DFS(L+1,R,cnt+1,v+val[L],x);
	return ;
}
void Init(){
	p.clear(),q.clear();
	memset(f,0,sizeof(f));
	memset(g,0,sizeof(g));
	memset(bar,0,sizeof(bar));	
	return ;
}
int main(){//至多有i個great的個數h
	Prepare();
	int T=read();
	while(T--){
		Init();
		int n=read(),cnt=0,Max=read();
		for(int i=1;i<=n;i++){
			val[i]=read();
			if(val[i]!=-1)
				cnt++;
		}
		sort(val+1,val+n+1,greater<int>());
		for(int t=0;t<=cnt;t++){
			memset(a,0,sizeof(a));
			for(int i=1;i<=n;i++)
				for(int j=i+1;j<=n;j++)
					if(j>cnt||j<=t)
						a[i][j]--,a[j][i]--,a[i][i]++,a[j][j]++;
			f[t]=Det(n);
			for(int i=0;i<t;i++)
				f[t]=(f[t]-C[t][i]*f[i]%Mod+Mod)%Mod;
		}
		DFS(1,cnt/2,0,0,p);
		DFS(cnt/2+1,cnt,0,0,q);
		sort(p.begin(),p.end()),sort(q.begin(),q.end());
		for(int i=0;i<(int)p.size();i++)
			bar[p[i].second]++;
		for(int i=0,j=(int)p.size()-1;i<(int)q.size();i++){
			while(~j&&q[i].first+p[j].first>Max)
				bar[p[j--].second]--;
			for(int k=0;k<=cnt/2;k++)
				g[q[i].second+k]=Add(g[q[i].second+k],bar[k]);
		}
		LL ans=0;
		for(int i=0;i<=cnt;i++)
			ans=(ans+1ll*f[i]*g[i])%Mod;
		printf("%lld\n",ans);
	}
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章