P8757 完美序列 题解
Demeanor_Roy · · 题解
-
原题链接
-
小清新组合数推导。
首先考虑对于一个确定的
不难发现,由于完美子序列后一项是前一项的因子,排列又没有重复元素,所以完美子序列每一项至少除以二,那最优的子序列当然是
确定完
首先一个直观也可说是省事的想法就是只有上面所说的最优子序列一种,可当
其实是不多的。从
这也很容易证明:因为完美子序列必须满足有
那么哪些
既然已经确定了有哪些类型的长度为
由于贡献是由子序列而来,而不是排列而来,所以我们只需要算出每种合法子序列的权值和,乘上它们在排列中的出现次数,再加起来就行,不需要担心因为两个合法子序列出现在一个排列而算重。
首先看次数,因为每种合法子序列长度相同,且都是在排列中,所以在所有排列中出现的次数也应相同。考虑
接下来看权值和,首先最优子序列的权值和好说,就是
其中外层
接下来再利用一些基本的和式技巧,就可以得到以下答案:
那到此为止,这道题就完成了,时间复杂度
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N=1e6+10,mod=1e9+7;
int T,Log[N],pw[N],fct[N],inv[N],finv[N];
inline void init()
{
pw[0]=fct[0]=inv[0]=finv[0]=fct[1]=inv[1]=finv[1]=1;
pw[1]=2;
for(int i=2;i<N;i++)
{
Log[i]=Log[i/2]+1;
pw[i]=pw[i-1]*2%mod;
fct[i]=(LL)fct[i-1]*i%mod;
inv[i]=(mod-(LL)mod/i*inv[mod%i]%mod)%mod;
finv[i]=(LL)finv[i-1]*inv[i]%mod;
}
}
inline LL C(int x,int y)
{
return (LL)fct[x]*finv[y]%mod*finv[x-y]%mod;
}
int main()
{
init();
scanf("%d",&T);
while(T--)
{
int n,x,ans;
scanf("%d",&n);
x=Log[n]+1;
ans=(LL)fct[n-x]*C(n,x)%mod*(pw[x]-1)%mod;
if(x>=2&&pw[x-2]*3<=n) ans=(ans+(LL)fct[n-x]*C(n,x)%mod*((((LL)x*3%mod-4+mod)%mod*pw[x-1]%mod-x+mod+2)%mod)%mod)%mod;
printf("%d\n",ans);
}
return 0;
}
- 完结撒花~