一次考试的中档题
戳(直接弄了个vjudge)
这道题一开始我求逆序对先是想枚举一个点,然后在去log的查询他前面的比他大的点的合法情况,虽然这是这道题最后的解法,但是未免有些冲动,因为还有很多细节与铺垫的东西没有考虑而直接跳到了最后一步。
但实际上,这也并不是没有指示作用。接下来,就会以这个想法为指引
这道题一个绕不过去的最基本的、现实的问题就是无论怎样都要知道合法方案怎么求。虽然并不难,但这作为一个套路,应该看到这个题就立马写出来,否则这道题连突破口都没有。
求合法方案显然不复杂。有很多种想法。最简单的就是映射集合法。
显然每一个[1-n]的数字都有一个可以放的集合,这个集合就是那些比他大的 ai a i ,每一个数字的集合显然是一个包含集,也就是小的数选择的范围里包含了大的数的选择范围,这样一来,最大的那一个数(也就是n)的集合就是所有大于等于它的 ai a i 的数量,次大的数(也就是n-1)就是所有大于等于他的数的数量减去1,1就是那个最大的数。根据上树推论即乘法原理,合法方案数就是
但是要处理出某一位为0的情况。首先,原前缀积如果出现0,那么最终的答案肯定是0.只考虑减1以后的前缀积,如果算出来某一个Data[i]为0,首先把这一位记成1,然后对于前缀积上的每一个数组开一个loc记录离自己最近的1出现在哪里。这样先通过loc判断查询的这一段区间值是否为0,然后再进行普通的计算。
一个n已经变成了1,另一个n要变成log。
对于枚举到了i,首先我们可以通过 Σ Σ 的数学推导,也可以通过感性理解得知用线段树维护前面比自己大的一个东西,比自己小的一个东西。这个东西究竟是什么?
对于前面一个 aj a j , aj<ai a j < a i ,首先在枚举到 aj a j 时,就会默认后边将要用到自己做贡献的是n,于是预先把A的1-aj乘上B的aj+1到n.如果每一个都这样做,那么枚举到ai时,知道那些比自己小的,把B的aj+1到n替换成A的aj+1到n.
这里还有一个除法,这样就意味着处理0的情况。刚才记录了loc,这里就可以查询 [loc[a[i]]+1,a[i]] [ l o c [ a [ i ] ] + 1 , a [ i ] ] 因为超过loc[a[i]]的肯定是0,不超过的肯定不是0
另一种自己推一下吧
#include
#define LL long long
using namespace std;
static char gc(){
static char buf[1<<6],*p1=buf,*p2=buf;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,1<<5,stdin),p1==p2)?EOF:*p1++;
}
template <class T>
inline void read(T&data){
data=0;
register char ch=0;
while(ch<'0'||ch>'9')ch=gc();
while(ch<='9'&&ch>='0'){
data=(data<<3)+(data<<1)+(ch&15);
ch=gc();
}
return;
}
const int _ = 200101,mod = 1e9+ 7;
int aa[_],n,Data[_],sum1[_],sum2[_],loc[_],LOC[_],qz[_],hz[_],sum[_];
inline int poww(register int ax,register int cc){
register int ret=1,Base=ax;
for(;cc;cc>>=1,Base=1LL*Base*Base%mod)if(cc&1)ret=1LL*ret*Base%mod;
return ret;
}
inline int Que1(register int l,register int r){ return 1LL*sum1[r]*poww(sum1[l-1],mod-2)%mod;}
inline int Que2(register int l,register int r){ return 1LL*sum2[r]*poww(sum2[l-1],mod-2)%mod;}
inline int lowbit(register int k){return k&(-k);}
inline int query1(register int l,register int r){
if(rreturn 0;
register int ret1=0,ret2=0;
for(register int i=l-1;i;i-=lowbit(i))ret1=(ret1+qz[i])%mod;
for(register int i=r;i;i-=lowbit(i))ret2=(ret2+qz[i])%mod;
return (ret2-ret1+mod)%mod;
}
inline int query2(register int l,register int r){
if(rreturn 0;
register int ret1=0,ret2=0;
for(register int i=l-1;i;i-=lowbit(i))ret1=(ret1+hz[i])%mod;
for(register int i=r;i;i-=lowbit(i))ret2=(ret2+hz[i])%mod;
return (ret2-ret1+mod)%mod;
}
inline int query3(register int l,register int r){
if(rreturn 0;
register int ret1=0,ret2=0;
for(register int i=l-1;i;i-=lowbit(i))ret1=(ret1+sum[i])%mod;
for(register int i=r;i;i-=lowbit(i))ret2=(ret2+sum[i])%mod;
return (ret2-ret1+mod)%mod;
}
inline void fix(register int loc,register int zh1,register int zh2){
for(register int i=loc;i<=n;i+=lowbit(i))qz[i]=(qz[i]+zh1)%mod,hz[i]=(hz[i]+zh2)%mod,sum[i]++;
return;
}
int main(){
freopen("belief.in","r",stdin);
freopen("belief.out","w",stdout);
read(n);
for(register int i=1;i<=n;++i)read(aa[i]),aa[i]=aa[i]>n?n:aa[i],++Data[aa[i]];
for(register int i=n-1;i>=1;--i)Data[i]=Data[i]+Data[i+1];
for(register int i=1;i<=n;++i)Data[i]=Data[i]-n+i;
for(register int i=1;i<=n;++i)if(Data[i]<0){cout<<0<return 0;}
sum1[0]=sum2[0]=1;
for(register int i=1;i<=n;++i)sum1[i]=1LL*sum1[i-1]*Data[i] %mod;
if(!sum1[n]){puts("0");return 0;}
for(register int i=1;i<=n;++i){
--Data[i];
if(Data[i]==0)loc[i]=i,sum2[i]=sum2[i-1];
else loc[i]=loc[i-1],sum2[i]=1LL*sum2[i-1]*Data[i]%mod;
}
LOC[n+1]=n+1;
for(register int i=n;i>=1;--i)
if(!Data[i]) LOC[i]=i;
else LOC[i]=LOC[i+1];
register int ans=0,ni2=poww(2,mod-2);
for(register int i=1;i<=n;++i){
register int f=1LL*query1(loc[aa[i]]+1,aa[i])*poww(Que2(aa[i]+1,n),mod-2)%mod*Que1(aa[i]+1,n)%mod;
ans=(ans+1LL*f*ni2%mod)%mod;
f=1LL*query2(aa[i]+1,LOC[aa[i]+1]-1)*poww(Que2(1,aa[i]),mod-2)%mod*Que1(1,aa[i])%mod;
f=1LL*f*ni2%mod;
ans=(ans-f+mod)%mod;
f=query3(aa[i]+1,n);
ans=(ans+1LL*f*sum1[n]%mod)%mod;
fix(aa[i],1LL*Que1(1,aa[i])*Que2(aa[i]+1,n)%mod,1LL*Que2(1,aa[i])*Que1(aa[i]+1,n)%mod);
}
cout<