版权声明:本文为博主原创文章,未经博主允许不得转载。 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;
}