emmm..终于ac了,这里介绍一下标程做法,大佬的分治做法我也看不太懂,码风完全不一样.
标程就是从后往前计算贡献,算出贡献的付出,最后保留的贡献就是f[n].
#include <bits/stdc++.h>
typedef long long ll;
const ll mod=998244353;
const int N=5e5+5;
struct vv{
ll h,id;//高度和位子.
}w[N];
ll n,dis[N],g[N],f[N],sum[2][N];//第1维维护个数,第二维维护权值.
ll qp(ll a,ll b)
{
ll res=1;
while(b)
{
if(b&1) res=res*a%mod;
b>>=1;
a=a*a%mod;
}return res;
}
ll iv(ll x) { return qp(x,mod-2); }
bool cmp(vv a,vv b) { return a.h<b.h; }
ll lowbit(ll x) { return x&(-x); }
void add(ll pos,ll val,int op)
{
while(pos<=n)
{
sum[op][pos]+=val;
pos+=lowbit(pos);
}
}
ll query(ll pos,int op)
{
ll res=0;
while(pos)
{
res+=sum[op][pos];
pos-=lowbit(pos);
}return res;
}
ll ask(ll pos)//查询前面所有点到这个点的距离.
{
ll res=0;
//询问在它前面的距离.
res=((res+query(pos-1,0)*pos-query(pos-1,1))%mod+mod)%mod;
//询问在它后面的距离.
res=((res+query(n,1)-query(pos,1)-(query(n,0)-query(pos,0))*pos)%mod+mod)%mod;
return res;
}
signed main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&w[i].h);
w[i].id=i;
}
std::sort(w+1,w+1+n,cmp);
//进行dp.
int cnt=0;//统计前面所有的平均值.
for(int i=1;i<=n;)
{
int j=i,now=0;//单纯统计当前转移的g[i]平均值.
while(w[i].h==w[j].h) j++;
for(int k=i;k<j;k++)
{
dis[k]=(ask(w[k].id)%mod+mod)%mod;
g[k]=((cnt+dis[k])%mod+mod)%mod;
g[k]=((g[k]*iv(i-1))%mod+mod)%mod;
now=((now+g[k])%mod+mod)%mod;
}
for(int k=i;k<j;k++) {int pos=w[k].id;add(pos,1,0);add(pos,pos,1);}
for(int k=i;k<j;k++)
{
ll odd=((now-g[k]+ask(w[k].id)-dis[k])%mod+mod)%mod;
f[k]=((iv(j-2)*(i-1)%mod)*g[k]%mod+iv(j-2)*odd%mod+mod)%mod;
cnt=((cnt+f[k])%mod+mod)%mod;
}
//概率dp都是倒过来dp的.
i=j;
}
printf("%lld\n",f[n]);
}

京公网安备 11010502036488号