团队题库链接:https://www.luogu.org/problemnew/show/T24295
逆序对Plus
题目背景
LPA AC了逆序对一题感觉不过瘾, 于是ShadyPi给他出了这样一道题。
题目描述
给定一个序列a[1], a[2], a[3] …, a[n] a[1],a[2],a[3]…,a[n] , 求满足a[i] < a[j] > a[k]a[i]
输入输出格式
输入格式:
第一行一个正整数n, 表示序列长度为n。 第二行n个正整数a[i], 表示序列中每个元素的值。
输出格式:
一个正整数a, 表示答案为a。
输入输出样例
输入样例#1:
5
1 2 3 4 1
输出样例#1:
6
说明
对于20%的数据, n<=10000n<=10000
对于50%的数据, n <= 100000n<=100000 ;
对于100%的数据, n <= 400000n<=400000 , 0 < a[i] <= 21474836470
题解
正解是建两次值域线段树,但是博主不晓得什么值域线段树,就用普通线段树艹过去了。。。
下面以统计 a[i]<a[j],i<j a [ i ] < a [ j ] , i < j 为例讲解:
我们只需要按权值大小排一次序,从小到大插入每个点,线段树维护区间内已经插入的点数,每次先查找该点左边插入点的数目,因为是从小到大插的所以此时左边插入的点数就等于 a[i]<a[j],i<j a [ i ] < a [ j ] , i < j 的数目。
值得注意的是,排序的时候我们要把权值相同的节点中位置靠后的放在前面,把位置靠后的先插进树里,这样才不会把权值相同的在自己前面的点统计进去。
另外一半类似,大家结合左半边和代码理解吧。
代码
#include<bits/stdc++.h>
using namespace std;
const int M=1e6+5;
const int mod=666233;
struct sd{
int val,pos;
};
long long tree[M<<1],ans[M],cot[M],tong[M],n;
sd x[M];
bool cmp1(sd a,sd b){return a.val==b.val?a.pos>b.pos:a.val<b.val;}
bool cmp2(sd a,sd b){return a.val==b.val?a.pos<b.pos:a.val<b.val;}
void up(int v)
{tree[v]=tree[v<<1]+tree[v<<1|1];}
void in()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&x[i].val),x[i].pos=i;
}
void ins(int v,int pos,int le,int ri)
{
if(le==ri&&le==pos)
{
tree[v]=1;
return;
}
int mid=(le+ri)>>1;
if(pos<=mid)ins(v<<1,pos,le,mid);
else ins(v<<1|1,pos,mid+1,ri);
up(v);
}
long long ques(int v,int le,int ri,int ll,int rr)
{
if(ll<=le&&ri<=rr)return tree[v];
int mid=(le+ri)>>1,ans=0;
if(ll<=mid)ans=ques(v<<1,le,mid,ll,rr);
if(rr>mid)ans+=ques(v<<1|1,mid+1,ri,ll,rr);
return ans;
}
void ac()
{
sort(x+1,x+1+n,cmp1);
for(int i=1;i<=n;++i)
{
ans[x[i].pos]=ques(1,1,n,1,x[i].pos);
ans[x[i].pos]%=mod;
ins(1,x[i].pos,1,n);
}
sort(x+1,x+1+n,cmp2);
memset(tree,0,sizeof(tree));
for(int i=1;i<=n;++i)
{
ans[x[i].pos]*=ques(1,1,n,x[i].pos,n);
ans[x[i].pos]%=mod;
ins(1,x[i].pos,1,n);
}
long long hh=0;
for(int i=1;i<=n;++i)
hh+=ans[i],hh%=mod;
printf("%lld",hh);
}
int main()
{
in();ac();
return 0;
}