楼兰图腾
题意:给你n个点分别为(i,y[i])
如果三个点(i,yi),(j,yj),(k,yk)(i,yi),(j,yj),(k,yk)满足1≤i<j<k≤n且yi>yj,yj<yk1≤i<j<k≤n且yi>yj,yj<yk,则称这三个点构成V图腾;
如果三个点(i,yi),(j,yj),(k,yk)(i,yi),(j,yj),(k,yk)满足1≤i<j<k≤n且yi<yj,yj>yk1≤i<j<k≤n且yi<yj,yj>yk,则称这三个点构成∧图腾;
求V和^图腾的个数。
思路:通过树状数组求逆序对的方法求得答案。树状数组求逆序对就是维护一个a[i]范围的区间,进行区间内+1操作,这样后面的后缀多一个,再通过逆序对的性质进行求解。
比如V先1~n遍历找每个位置多少个数比当前大,再n~1遍历找每个位置多少个数比当前小。^亦如此。
和算法竞赛指南上的求逆序对不太一样,因为它是找比其小的,效果还是一样的。
#include<cstring> #include<algorithm> #include<vector> #include<map> #include<queue> #include<cstdio> #include<cmath> #define ll long long #define lowbit(x) x&(-x) using namespace std; const int N=2e5+10; ll ans,a[N],c[N],l[N],r[N],n,max_val; ll ask(ll x) { ll ans=0; for(;x;x-=lowbit(x)) ans+=c[x]; return ans; } void add(ll x,ll y) { for(;x<=n;x+=lowbit(x)) c[x]+=y; } ll sum_ans() { ll ans=0; for(int i=1;i<=n;i++) { ans+=l[i]*r[i]; } return ans; } int main() { scanf("%lld",&n); for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); max_val=max(max_val,a[i]); } for(ll i=n;i;i--) { r[i]=ask(max_val)-ask(a[i]); add(a[i],1); } memset(c,0,sizeof(c)); for(ll i=1;i<=n;i++) { l[i]=ask(max_val)-ask(a[i]); add(a[i],1); } ll sum1=sum_ans(); memset(c,0,sizeof(c)); for(ll i=n;i;i--) { r[i]=ask(a[i]-1); add(a[i],1); } memset(c,0,sizeof(c)); for(ll i=1;i<=n;i++) { l[i]=ask(a[i]-1); add(a[i],1); } ll sum2=sum_ans(); printf("%lld %lld\n",sum1,sum2); }