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

题目


分析

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

\[\sum_{i=1}^nC(n,i)\times i=n\sum_{i=1}^nC(n-1,i-1)=n\sum_{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;
}
posted @ 2021-11-05 20:57  lemondinosaur  阅读(30)  评论(0编辑  收藏  举报