2..3...4.... Wonderful! Wonderful!
题目描述
有一个元素等于其下标的数组,长度为n,对于属于区间\([1,(n-1)/2]\)的每一个数,我们称其为k,我们可以对数组进行任意次数的操作。
操作:选择长度为\(2*k+1\)的子序列,然后只留下最中间的那个数,删掉其他的元素。
我们想知道对于每个\(k\),操作后,最后有多少个可能得到的数组。
数据范围
\(t\)组数据,每组就一个\(n\),$n $的总和不大于\(1e6\)。
解法
我们设x为总共要删掉的数量,会发现最后能得到的数组,有以下特征。
1,x为\(2k\)的倍数,这一点显然。
2,存在一个元素,它的两边有至少k个要删掉的元素。
我们证明以下,对于2中存在的元素,我们看他的两边,如果一两边的数量大于k,我们就一定能选择一个元素作为删除操作中的中心元素,然后成功删掉大于k的部分元素。过了一段时间后,就会出现只剩下\(2k\)个元素的情况,然后就能一次删掉了。
搞清楚以上的局面,我们需要再发现,正面求出这些还是很困难的,于是我们求出总数\(C(n,x)\)后,再减去不合法的即可。需要注意的是, x是要枚举的,但x为2k的倍数,所以其复杂度为调和级数。
现在我们先将要删掉的元素排成一排,然后往里面插入那些不删掉的。想要不合法,全向左右两边的\(k-1\)个元素扎入即可。于是问题就转化成了有n-x个相同的小球,要插入2k个相同盒子,允许盒子有空的,问有多少种情况。答案就是\(C(n-x+2*k-1,2*k-1)\)。这里为什么小球和盒子都是相同的呢?因为元素的顺序是已经定下的。
代码实现
const int mod=998244353;
const int maxn=1000010;
int fect[maxn], infect[maxn];
ll binpow(ll a,ll b,ll c){
ll ans=1;
while(b){
if(b&1)
ans=(Int)ans*a%c;
a=(Int)a*a%c;
b>>=1;
}
return ans;
}
int C(int a,int b){
return fect[a]*infect[b]%mod*infect[a-b]%mod;
}
void initzuhe(int n){
fect[0]=1;
infect[0]=1;
for(int i=1;i<=n;i++){
fect[i]=(fect[i-1]*i)%mod;
}
infect[n]=binpow(fect[n],mod-2,mod);
for(int i=n-1;i>=1;i--)
infect[i]=infect[i+1]*(i+1)%mod;
}
ll cul(int n,int k){
ll ans=0;
for(int x=2ll*k;x<=n;x+=2ll*k){
ans=(ans+C(n,x)-C(n-x-1+2ll*k,2ll*k-1)+mod)%mod;
}
return ans;
}
void solve(){
int n;cin>>n;
for(int k=1;k<=(n-1)/2ll;k++)cout<<(cul(n,k)+1ll)%mod<<" ";
cout<<"\n";
}
signed main(){
std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
initzuhe(1000000);
//srand((unsigned)time(NULL));
int t;std::cin>>t;while(t--)
solve();
}
标签:...,删掉,int,题解,ll,元素,Wonderful,infect
From: https://www.cnblogs.com/shi5/p/18034882