专栏文章
CF2127F题解
CF2127F题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @miofd0p6
- 此快照首次捕获于
- 2025/12/02 18:18 3 个月前
- 此快照最后确认于
- 2025/12/02 18:18 3 个月前
先考虑答案的形式:这个东西很像是 ,但是看到样例发现不对, 是分段的值,于是答案是每段的 之和( 是段长。)。
我们考虑分析更深层次的东西。考虑之前的东西等价于枚举统计所有的方案求和。但是“和” 里面的东西是可以拆开的。我们发现:
-
每个和都可以写成 的形式,其中 。
-
答案是 ,可以写成 ,简而言之,就是可以枚举这一项系数和值求有多少个方案是这样的和。
考虑所谓“和” 有啥:
+ 和 -。 是统计到
+ 的当且仅当 是某一段的末尾,就是 最大且 ,这个定义包括 。 是统计到
- 的当且仅当 是某一段的开头,就是 或者前一项是最大值且 。注意 的时候是 且最大。先算第一项:我们发现本质不同的答案东西是 位置和 的位置。考虑枚举这个值是啥,对于 ,答案是 。对于 则是 。其中 是长度为 的序列中最大值不超过 ,和为 的方案数,通过容斥多少不满足条件的容易知道是:
单次计算的运算量是 的,注意到在上面的求和中这个的和是不超过 就是 的,然后是前面 和最后 两次算,因此直接计算即可。
现在考虑
- 的贡献,对于第一个数,就是要求最后一个数比它大。枚举最后一个数是啥,这个的式子是:显然不能直接算,展开 得到:
令
则变成:
对于中间位置的数,存在前面的数了也要被算进去,则式子是:
也可以推导出:
其中 。当然别忘了乘以 。
最后是最后一个数的和,直接算 即可。
其实题解中提到更好的方法是:注意到两个数相邻的限制是即使不相邻也是这个答案,因此对于所有非最大位置的答案相同,因此求和后除法可行。因此可以直接枚举 ,对于第一个数,答案是 ,对于中间项,答案是:。
时间复杂度 ,注意可能需要特判 ,因为可能会算出 的组合数。
CPPmint g(int n,int y,int z){
mint ans=0;
for(int k=0;y-k*(z+1)>=0;k++){
mint prep=math.binom(n,k)*math.binom(y-k*(z+1)+n-1,n-1);
if(k&1){
ans-=prep;
}else{
ans+=prep;
}
}
return ans;
}
mint f(int a,int b,int c){
//sum i=1^a \binom b-i c
return mint(b+1)*(math.binom(b,c+1)-math.binom(b-a,c+1))-mint(c+1)*(math.binom(b+1,c+2)-math.binom(b-a+1,c+2));
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int t;
cin>>t;
while(t--){
int n,m;
cin>>n>>m;
if(n==2){
mint ans=0;
for(int a=0;a<=m-a;a++){
ans+=m-a;
ans-=a;
}
cout<<ans<<'\n';
continue;
}
mint ans=0;
for(int i=1;i<=m;i++){
ans+=mint(i)*g(n-1,m-i,i);
}
for(int i=1;i<=m/2;i++){
ans+=mint(i)*g(n-2,m-2*i,i)*mint(n-1);
}
for(int sum=0;sum<=m;sum++){
for(int k=0;m-sum-k*(sum+1)>=0;k++){
mint pc=math.binom(n-2,k);
if(k%2==0)pc=-pc;
int a=min({m-sum,sum,m-sum-k*(sum+1)}),b=m-sum-k*(sum+1)+n-3,c=n-3;
mint wc=f(a,b,c);
ans+=pc*wc;
}
}
for(int sum=0;2*sum<=m;sum++){
for(int k=0;m-2*sum-k*(sum+1)>=0;k++){
mint pc=math.binom(n-3,k)*mint(n-2);
if(k%2==0)pc=-pc;
int a=min({sum,m-2*sum,m-2*sum-k*(sum+1)}),b=m-2*sum-k*(sum+1)+n-4,c=n-4;
mint wc=f(a,b,c);
ans+=pc*wc;
}
}
for(int i=1;2*i<=m;i++){
ans-=mint(i)*g(n-2,m-2*i,i);
}
cout<<ans<<'\n';
}
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...