UPC 5727 小奇遐想

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Seeyouer/article/details/81268357

题目描述

撷来一缕清风飘渺
方知今日书信未到
窗外三月天霁垂柳新长枝条
风中鸟啼犹带欢笑
——《清风醉梦》
小奇望着青天中的悠悠白云,开始了无限的遐想,在它的视野中,恰好有n朵高度不同的白云排成一排,他想从左到右选出四朵白云a,b,c,d,使得h_a<h_b<h_d<h_c,即看起来像是彩虹的形状!它想知道有多少种方案数。

输入

第一行包括1个整数n。
第二行包括n个整数,第i个正数表示h_i,保证这n个整数是n的一个全排列。

输出

输出一个整数表示答案。(mod 16777216)

样例输入

5
1 5 3 2 4

样例输出

0

提示

对于10%的数据n<=600;对于40%的数据n<=5000;
对于100%的数据n<=200000。

题目大意就是,给你一个1~n的全排列,让你求1243这种排列的数量。

首先,咱们应该怎么求更简单,1234这种序列求起来肯定比1243容易,所以咱就用12XX(XX都是大于2的)的数量-1234的数量=1243的数量

  • 求12XX
    先计算出每个数前面比她小的数的数量lmax,和后面比她大的数的数量rmax,然后a[0]~a[n-1]枚举,把每个数当作2,求出的结果就是lmax*C(rmax,2)=lmax*ramx* (rmax-1)/2
  • 求1234
    先对每一个a[i]求从0~i上升子序列数为3(相当于123)的 序列数量,然后再乘上rmax(相当于4)

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=16777216;
int n,a[201010];
ll c[202020]={0},l[202020],r[202020];
void add(int k,ll num) //a[k]的值增加num
{
    while(k<=n)   //a[k]的上司都要加上num
    {
        c[k]=(c[k]+num)%mod;
        k+=k&-k;    //k累加上lowbit就得到上司的下标
    }
}
ll read(int k)//1~k的区间和
{
    ll sum=0;
    while(k)
    {
        sum=(sum+c[k])%mod;
        k-=k&-k;  //这里是逆序的,所以累减
    }
    return sum;
}

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);

    }
    for(int i=1;i<=n;i++){
        l[i]=read(a[i]);//前面比他小的个数

        r[i]=a[i]-1-l[i];//后面比他小的个数
        //cout<<l[i]<<" "<<r[i]<<" i "<<i<<" ai"<<a[i]<<endl;
        add(a[i],1);
    }
    ll ans1=0;
    for(int i=1;i<=n;i++){
        ll rmax=n-i-r[i];//后面比他大的个数
        ans1=(ans1+l[i]*rmax*(rmax-1)/2)%mod;
    }
    memset(c,0,sizeof(c));
    ll ans2=0;
    for(int i=1;i<=n;i++){
        ans2=(ans2+read(a[i])*(n-i-r[i])%mod)%mod;//read(a[i])为求123的数量
        add(a[i],l[i]);
    }
    cout<<(ans1-ans2+mod)%mod;//记住加mod再模除
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Seeyouer/article/details/81268357
UPC