CF1753C-Wish-I-Knew-How-to-Sort题解
题意:有一个 01 序列,每次可以选择两个元素,如果为逆序则交换,否则不变,无论是否交换都算一次操作。问排好序的期望操作次数。
容易想到使用 DP 计算,但状态并不是很好想。首先状态必须有单向性,必须有严格的 DP 顺序,于是我们可以想到用逆序对数来记录状态。然而,思考会发现并不可行。不仅是复杂度无法接受,仅用逆序对也无法完整地表示状态。实际上,我们可以用“未归位的数字个数”来作为状态。具体地,假设当前序列为
做法 1
我们定义
我们令
此即为 DP 方程。
By cxm1024
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
int ksm(int a,int b,int res=1) {
for(;b;a=a*a%mod,b>>=1)
if(b&1) res=res*a%mod;
return res;
}
int inv(int x) {return ksm(x,mod-2);}
int a[200010],f[200010];
void Solve() {
int n,cnt0=0,cnt1=0;
cin>>n;
for(int i=1;i<=n;i++) {
cin>>a[i];
if(a[i]==0) cnt0++;
}
for(int i=1;i<=cnt0;i++)
if(a[i]==1) cnt1++;
f[0]=0;
int invall=inv(n*(n-1)%mod);
for(int i=1;i<=cnt1;i++) {
int p1=2*i*i%mod*invall%mod;
int p2=(n*(n-1)%mod-2*i*i%mod+mod)%mod*invall%mod;
f[i]=p1*(p2*inv((1-p2+mod)%mod*((1-p2+mod)%mod)%mod)%mod+(f[i-1]+1)*inv((1-p2+mod)%mod)%mod)%mod;
}
cout<<f[cnt1]<<endl;
}
signed main() {
int T=1;
cin>>T;
while(T--) Solve();
return 0;
}
做法 2
上面的做法显然过于麻烦了,我们考虑简单一点的转移方式。有一个经典的套路,就是用“从自身转移到自身”的方式来解决无穷级数的问题。在此题中,我们只考虑第一次操作。如果第一次操作恰好选中了该选的数,则为
在此方法下,最终的转移方程如此的简单,只是一个前缀和的形式。
By cxm1024
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
int ksm(int a,int b,int res=1) {
for(;b;a=a*a%mod,b>>=1)
if(b&1) res=res*a%mod;
return res;
}
int inv(int x) {return ksm(x,mod-2);}
int a[200010],f[200010];
void Solve() {
int n,cnt0=0,cnt1=0;
cin>>n;
for(int i=1;i<=n;i++) {
cin>>a[i];
if(a[i]==0) cnt0++;
}
for(int i=1;i<=cnt0;i++)
if(a[i]==1) cnt1++;
f[0]=0;
int all=n*(n-1)%mod;
for(int i=1;i<=cnt1;i++)
f[i]=(f[i-1]+all*inv(2*i*i%mod)%mod)%mod;
cout<<f[cnt1]<<endl;
}
signed main() {
int T=1;
cin>>T;
while(T--) Solve();
return 0;
}
容易发现,该前缀和出去分子后与
By cxm1024
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
int ksm(int a,int b,int res=1) {
for(;b;a=a*a%mod,b>>=1)
if(b&1) res=res*a%mod;
return res;
}
int inv(int x) {return ksm(x,mod-2);}
int a[200010],f[200010];
signed main() {
ios::sync_with_stdio(false);
for(int i=1;i<=200000;i++)
f[i]=(f[i-1]+inv(2*i*i%mod))%mod;
int T=1;
cin>>T;
while(T--) {
int n,cnt0=0,cnt1=0,ans=0;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cnt0+=(a[i]==0);
for(int i=1;i<=cnt0;i++) cnt1+=(a[i]==1);
cout<<f[cnt1]*(n*(n-1)%mod)%mod<<endl;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步