5727: 小奇遐想

本文介绍了一种利用树状数组求解特定升序和降序子序列数量的方法,通过定义l[i]为以i结尾长度为2的升序子序列数量,r[i]为以i为起点长度为2的降序子序列数量,进而计算出长度为3的升序子序列数量,并给出完整的C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

思路:树状数组,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 <stdio.h>
#include <algorithm>
#include <string.h>
#define ll long long
#define pa pair<int,int>
#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)的求模运算。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

2020/3/16

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值