凸多边形 [动态规划, 计数]
一个 多边形 若为 凸多边形, 则需满足: 除去 最大边 所有边的权值和要 大于 最大边 的权值 .
设 表示前 个数字, 凑成 的方案数,
表示前 个数字, 凑成 的所有方案的权值和, 状态转移 如下:
最大边 为 的方案数为 , , 时间复杂度 .
考虑 补集转换, 对 最大边 , 计算 , 然后使用 “总方案权值和”: 减去 得到 ,
现在考虑如何快速求 , 其意义为计算出 必须被选, 所有挑选数字方案的权值和, 再加起来,
对每个 , 其对 贡献为 , 所以 .
特别地, 对 的贡献为 .
可以 预处理, 于是 总时间复杂度 .
#include<bits/stdc++.h>
#define reg register
const int maxn = 1005;
const int mod = 1e9 + 7;
int N;
int pw[maxn];
int tot[maxn];
int f[maxn][maxn];
int g[maxn][maxn];
int gsum[maxn][maxn];
int Ksm(int a, int b){ int s=1; while(b){ if(b & 1) s = 1ll*s*a%mod; a=1ll*a*a%mod; b >>= 1; } return s; }
void Init(){
f[0][0] = 1;
pw[0] = 1; for(reg int i = 1; i < maxn; i ++) pw[i] = 2ll*pw[i-1] % mod;
for(reg int i = 1; i < maxn; i ++)
for(reg int j = 0; j < maxn; j ++){
f[i][j] = f[i-1][j], g[i][j] = g[i-1][j];
if(j >= i){
f[i][j] += f[i-1][j-i];
if(f[i][j] >= mod) f[i][j] -= mod;
g[i][j] += (g[i-1][j-i] + f[i-1][j-i]) % mod;
if(g[i][j] >= mod) g[i][j] -= mod;
}
}
for(reg int i = 0; i < maxn; i ++)
for(reg int j = 0; j < maxn; j ++){
if(j) gsum[i][j] += gsum[i][j-1];
gsum[i][j] += g[i][j];
if(gsum[i][j] >= mod) gsum[i][j] -= mod;
gsum[i][j] += f[i][j];
if(gsum[i][j] >= mod) gsum[i][j] -= mod;
}
tot[1] = 1;
for(reg int i = 2; i < maxn; i ++){
tot[i] = tot[i-1];
tot[i] += (1ll*pw[i-2]*(i-1)%mod + pw[i-1]) % mod;
if(tot[i] >= mod) tot[i] -= mod;
}
}
void Work(){
scanf("%d", &N);
int res = 0;
for(reg int i = 1; i <= N; i ++){
res += gsum[i-1][i];
if(res >= mod) res -= mod;
}
int Ans = (tot[N]-res+mod) % mod;
Ans = 1ll*Ans*Ksm(Ksm(2, N), mod-2) % mod;
printf("%d\n", Ans);
}
int main(){
Init();
int T; scanf("%d", &T);
while(T --) Work();
return 0;
}