5727: 小奇遐想

题目链接

思路:树状数组,l[i]表示,表示 i 处结尾的长度为2的升序子序列种数(即正序对数量),

    r[i]表示表示, i 处为开头向后,长度为2的降序子序列数量(正序对数量)

12××:l[i]*C((n-i-r[i]),2);

1234:长度为三的升序子序列种数*(n-i-r[i]);

所以,1243=12××-1234;

参考源自这里

#include 
#include 
#include 
#define ll long long
#define pa pair
#define P 16777216
using namespace std;
int n;
int a[200005];
ll l[200005],r[200005],t[200005];
void add(int x,int y)
{
   for(int i=x;i<=n;i+=(i&-i))
      t[i]+=y;
}
ll query(int x)
{
    ll ans=0;
    for(int i=x;i>0;i-=(i&-i))
      ans+=t[i];
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
       scanf("%d",&a[i]);
       //query(a[i])计算的是以i为结尾长度为二的升序子序列种数
       l[i]=query(a[i]);
       r[i]=a[i]-l[i]-1;
       add(a[i],1);
    }
    ll res=0;
    for(int i=1;i<=n;i++)
      res=(res+l[i]*(n-i-r[i])*(n-i-1-r[i])/2)%P;
    ll pre=0;
    memset(t,0,sizeof(t));
    for(int i=1;i<=n;i++){
    //query(a[i])计算的是i为结尾长度为三的上升序子序列数
    //因为t[]先清零,后储存的是到i长度为二的上升子序列的种数,再加上它自己所以是长度为三的
      pre=(pre+query(a[i])*(n-i-r[i]))%P;
      add(a[i],l[i]);
    }
    printf("%lld\n",(res-pre+P)%P);
    return 0;
}

get到的取模新方法:a%p==a&(p-1),使用前提是p为2的幂次方。

这样,p-1 的二进制就是一个形如“0000111...”的数,比如,如果p=4p-1=3,其二进制表示为“0011”,如果p=8p-1=7,其二进制表示为“0111”,以此类推。将ap-1进行按位与运算的时候,由于高位都是0,只有低位的1才能决定最终的结果,就相当于对a进行相应数值(即p)的求模运算。

你可能感兴趣的:(还需思考,树状数组)