LOJ6072苹果树

https://loj.ac/problem/6072

虽然结合了很多算法,但是一步一步地推一下还不算太难的一道题。

首先考虑枚举枚举有用的苹果的集合,然后去算生成树个数。

先考虑怎么计算生成树个数。

发现可以使用matrix-tree。

所有有用点可以和有用点以及坏点连边,所有不是坏点的无用点只能和坏点连边,坏点可以和所有点连边。

然后跑一下matrix-tree。但是这样算出来的是至多S这个集合为有用点的方案数,需要套一个容斥。

分析上述过程,发现需要的信息只有好点的个数。

因此可以只需要计算出大小为k的和<lim合法集合有多少即可,这个显然可以meet in the middle。

#include<bits/stdc++.h>
#define N 44
#define M 1100000
#define ll long long
using namespace std;
inline int read()
{
	char ch=0;
	int x=0,flag=1;
	while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*flag;
}
const int mo=1e9+7;
int ksm(int x,int k)
{
	int ans=1;
	while(k)
	{
		if(k&1)ans=1ll*ans*x%mo;
		k>>=1;x=1ll*x*x%mo;
	}
	return ans;
}
int inv(int x){return ksm((x%mo+mo)%mo,mo-2);}
int a[N][N];
int matrix_tree(int n)
{
	int ans=1;
	for(int i=1;i<=n;i++)if(!a[i][i])return 0;
	for(int i=1;i<=n;i++)
	{
		for(int j=i+1;j<=n;j++)
		{
			int t=1ll*a[j][i]*inv(a[i][i])%mo;
			for(int k=i;k<=n;k++)a[j][k]=(a[j][k]-(1ll*t*a[i][k]%mo))%mo;
		}
		ans=1ll*ans*a[i][i]%mo;
	}
	return (ans%mo+mo)%mo;
}
int w[N],sz[N],dp[N],ans[N],c[N][N],f[N/2][M];
bool cmp(int a,int b){return a>b;}
int main()
{
	int n=read(),lim=read(),cnt=0,res=0;
	for(int i=1;i<=n;i++)w[i]=read(),cnt+=(w[i]==-1);sort(w+1,w+n+1,cmp);
	int m=n-cnt,mid=m/2;
	for(int s=0;s<(1<<mid);s++)
	{
		int num=0,tot=0;
		for(int i=1;i<=mid;i++)if(1<<(i-1)&s)num++,tot+=w[i];
		if(tot<=lim)f[num][++sz[num]]=tot;
	}
	for(int i=0;i<=mid;i++)sort(f[i]+1,f[i]+sz[i]+1);
	for(int s=0;s<(1<<(m-mid));s++)
	{
		int num=0,tot=0;
		for(int i=1;i<=m-mid;i++)if(1<<(i-1)&s)num++,tot+=w[i+mid];
		for(int i=0;i<=mid;i++)dp[i+num]=(dp[i+num]+(upper_bound(f[i]+1,f[i]+sz[i]+1,lim-tot)-f[i]-1))%mo;
	}
	for(int i=0;i<=n;i++){c[i][0]=1;for(int j=1;j<=n;j++)c[i][j]=(c[i-1][j-1]+c[i-1][j])%mo;}
	for(int o=0;o<=m;o++)
	{
		for(int i=1;i<=o;i++)for(int j=1;j<=n;j++)if(i==j)a[i][j]=(o-1)+cnt;else a[i][j]=-(j<=o||j>m);
		for(int i=o+1;i<=m;i++)for(int j=1;j<=n;j++)if(i==j)a[i][j]=cnt;else a[i][j]=-(j>m);
		for(int i=m+1;i<=n;i++)for(int j=1;j<=n;j++)if(i==j)a[i][j]=n-1;else a[i][j]=-1;
		ans[o]=matrix_tree(n-1);
		for(int i=0;i<o;i++)ans[o]=(ans[o]-(1ll*c[o][i]*ans[i]%mo))%mo;
		res=(res+(1ll*dp[o]*ans[o]%mo))%mo;
	}
	printf("%d",(res%mo+mo)%mo);
	return 0;
}
posted @ 2019-04-02 11:29  Creed-qwq  阅读(278)  评论(0编辑  收藏  举报