#01背包,容斥,排列组合#洛谷 5615 [MtOI2019]时间跳跃 分析 代码

题目


不是凸多边形当且仅当边数小于2或者最长边大于等于其余边之和,
那么容斥一下,首先总权值为

[sum_{i=1}^nC(n,i) imes i=nsum_{i=1}^nC(n-1,i-1)=nsum_{i=0}^{n-1}C(n-1,i)=n2^{n-1} ]

然后设 (f[n]) 表示等于 (n) 的方案数,(dp[n]) 表示等于 (n) 的权值和,

那么 (dp[n]=dp[n-x]+f[n-x]) 就可以实现转移,每次枚举新的最长边时统计一下方案数即可


代码

#include <cstdio>
#include <cctype>
using namespace std;
const int N=5011,mod=1000000007;
const int i2=(mod+1)>>1;
int f[N],dp[N],two[N],ans[N],iwo[N];
int iut(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
void Mo(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
void Pro(int n){
	f[0]=two[0]=iwo[0]=1;
	for (int i=1;i<=n;++i) ans[i]=1;
	for (int i=1;i<=n;++i) iwo[i]=1ll*i2*iwo[i-1]%mod;
	for (int i=1;i<=n;++i){
		for (int j=1;j<=i;++j) Mo(ans[i],(dp[j]+f[j])%mod);
		for (int j=n;j>=i;--j) Mo(dp[j],(dp[j-i]+f[j-i])%mod);
		for (int j=n;j>=i;--j) Mo(f[j],f[j-i]);
	}
	for (int i=1;i<=n;++i) two[i]=2ll*two[i-1]%mod;
	for (int i=1;i<=n;++i) Mo(ans[i],ans[i-1]);
	for (int i=1;i<=n;++i) ans[i]=(1ll*i*two[i-1]%mod-ans[i]+mod)*iwo[i]%mod;
}
int main(){
	Pro(N-11);
	for (int T=iut();T;--T)
	    print(ans[iut()]),putchar(10);
	return 0;
}