o_o 当前时间是:

8:00:47 PM

 

洛谷 P10254 口吃

传送门

description

求恰有 k 个逆序对的 n 的排列的权值和。权值为 i=1npii

  • n300
  • k(n2)

solution

很重要的一个拆贡献的想法是:一个数 pi 的系数 i 拆成下标从 1i 的数中 pjpipj<pi 的两部分。两部分的权值和明显是可以直接相加得到答案的。

考虑 dp 第一部分的贡献。按照值域从大往小把 n1 插入排列,枚举插入位置即可算出逆序对和权值和的增加量。

具体地,设 fi,j 表示把 nni+1i 个数插入排列,得到 j 和逆序对的方案数, gi,j 表示权值和。

有转移:

  • fi,j=p=0i1fi1,jp
  • gi,j=p=0i1gi1,jp+fi1,jp(ni+1)(p+1)

第二部分贡献同理。

这样就可以时间复杂度 O(n2k) 做了。过不去。

整理一下式子可以发现可以前缀和掉一部分转移。

还有形如 fi1,jpp 的部分, 这个可以记后缀和数组 si=p=ikfi1,i(ki+1),结合上面的前缀和就可以 O(1) 转移了。

时间复杂度 O(nk)

hint

两部分贡献的 dp 只有 g 数组转移的一个系数不一样,可以直接把系数加起来合并。

需要滚动数组。

code

#include<bits/stdc++.h>
using namespace std;
using E=long long;
constexpr E mod=998244353;
int main(){
#ifdef zzafanti
freopen("in.in","r",stdin);
#endif // zzafanti
cin.tie(nullptr),cout.tie(nullptr)->sync_with_stdio(false);
int n,k;
cin>>n>>k;
E ans=0;
vector<vector<E>> f(2,vector<E>(k+1)),g(2,vector<E>(k+1)),sg(2,vector<E>(k+1)),sf(2,vector<E>(k+1));
vector<E> trans(k+1);
f[0][0]=1,g[0][0]=0;
for(int j=0; j<=k; j++){
sf[0][j]=1;
}
for(int i=1; i<=n; i++){
for(int j=k; ~j; j--){
trans[j]=(j==k?0:trans[j+1]);
trans[j]=(trans[j]+f[i-1&1][j]*(k-j+1))%mod;
}
for(int j=0; j<=k; j++){
f[i&1][j]=sf[i-1&1][j];
if(j-i>=0) f[i&1][j]-=sf[i-1&1][j-i];
E coef=n+i*i-2*i+1;
g[i&1][j]=sg[i-1&1][j]+coef*sf[i-1&1][j]%mod;
if(j-i>=0) g[i&1][j]-=sg[i-1&1][j-i]+coef*sf[i-1&1][j-i];
coef=n-2*i+1;
E dlt=(j-i+1>=0?trans[j-i+1]:trans[0])-(j<k?trans[j+1]:0)-(k-j)*1ll*(sf[i-1&1][j]-(j-i>=0?sf[i-1&1][j-i]:0));
dlt%=mod;
g[i&1][j]=(g[i&1][j]+coef*dlt)%mod;
sf[i&1][j]=((j==0?0:sf[i&1][j-1])+f[i&1][j])%mod;
sg[i&1][j]=((j==0?0:sg[i&1][j-1])+g[i&1][j])%mod;
}
}
//cout<<(f[n&1][k]%mod+mod)%mod<<endl;
cout<<(g[n&1][k]%mod+mod)%mod<<endl;
return 0;
}
posted @   zzafanti  阅读(101)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
浏览器标题切换
浏览器标题切换end
点击右上角即可分享
微信分享提示