限制价值生成树[1446-51nod][双搜][矩阵树]

文章目录

题目

51nod
在这里插入图片描述

思路

太傻了,并不会拆分问题
由于是完全图,原问题可以拆分成两个问题

  • 选出 x x g r e a t great 点的方案数,记为 g ( x ) g(x)
  • x x g r e a t great 构成合法生成树的方案数,记为 f ( x ) f(x)
    总方案数就是 f ( x ) g ( x ) \sum f(x)*g(x)
    假设有 c n t cnt 个点不是 1 -1
    假设选的 x x 个点为 1 x 1\sim x
    那么剩下的 c n t x cnt-x 个点必定和 1 -1 相连,并且不会连接前 x x 个点
    建图就很清晰了
    前面 x x 个点就和 1 -1 相连,矩阵树定理求出方案数记为 h ( x ) h(x)
    h ( x ) h(x) 表示至多有 x x g r e a t great 的方案数
    不难得出 f ( x ) = h ( x ) i = 0 x 1 C ( x , i ) f ( i ) f(x)=h(x)-\sum_{i=0}^{x-1}C(x,i)*f(i)
    g ( x ) g(x) 用二分搜索即可,合并答案要利用桶
    时间复杂度 O ( n n 2 + 1 l o g n + n 4 ) 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;
}

猜你喜欢

转载自blog.csdn.net/qq_37555704/article/details/107149086
今日推荐