标签:
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5792
4 2 4 1 3 4 1 2 3 4
1 0
给n个数,问有多少个四元组,满足a≠b≠c≠d1≤a<b≤n,1≤c<d≤n,
Aa<Ab,Ac>Ad
这里a,b,c,d两两不相等。
解题思路:
先找出逆序对和升序对,把两个得到的数乘起来在减去不合法的即可。
其中不合法的就是长度为3的,有一些重复计算了的减掉。
四个数组,lmax表示他左边有多少个比他大的,lmin表示左边有多少小的
rmax,rmin分别表示右边有多少大的和小的。
这里由于0≤Ai≤1e9,所以在传参不可能这么大,所以我们要预先的a[i]这个数组进行离散化处理。
离散化的处理方法就是:找到相应的序列,对他排序,在给每一个重新复制,从1开始,这样最多也就50000,。
这里做的原因是我们这个题目和数值没有关系,至于前后左右的大小有关。
详见代码。
#include <iostream> #include <cstdio> #include <cstring> #include <vector> #include <map> #include <algorithm> using namespace std; #define N 50010 #define ll long long int a[N],c[N]; int lmax[N],lmin[N],rmax[N],rmin[N]; vector<int>V; map<int,int>M; int lowbit(int k) { return k&((k)xor(k-1)); } void add(int num,int k) { while (k<N) { c[k]+=num; k+=lowbit(k); } } int sum(int k) { int s=0; while (k) { s+=c[k]; k-=lowbit(k); } return s; } int main() { int n; ll s1,s2,s,cnt,ans; while (~scanf("%d",&n)) { s1=s2=cnt=0; V.clear(); M.clear(); for (int i=1; i<=n; i++) { scanf("%d",&a[i]); V.push_back(a[i]); } sort(V.begin(),V.end()); V.erase(unique(V.begin(),V.end()),V.end());//去重,把整个区间内重复的数都当成一个数 for (int i=0;i<V.size();i++) { M[V[i]]=i+1;//给每个数重新编号 } for (int i=1;i<=n;i++) a[i]=M[a[i]]; memset(c,0,sizeof(c)); for (int i=1; i<=n; i++) { add(1,a[i]); lmin[i]=sum(a[i]-1);//当前位置左边比他小的有多少个 lmax[i]=sum(N-1)-sum(a[i]);//已经出现的数肯定是他左边的数,在这个时候记录数轴上有多少个数再减去小于等于的,就是大于的了 } memset(c,0,sizeof(c)); for (int i=n; i>=1; i--) { add(1,a[i]); rmin[i]=sum(a[i]-1); rmax[i]=sum(N-1)-sum(a[i]); } for (int i=1; i<=n; i++) { s1+=lmin[i];//升序的对数 s2+=lmax[i];//逆序的对数 } s=s1*s2; // cout<<s<<endl; for (int i=1;i<=n;i++) { cnt+=lmax[i]*lmin[i]+rmax[i]*rmin[i]+lmax[i]*rmax[i]+lmin[i]*rmin[i]; } //cout<<cnt<<endl; ans=s-cnt; printf ("%lld\n",ans); } return 0; }
hdu 5792 World is Exploding(2016 Multi-University Training Contest 5——树状数组)
标签:
原文地址:http://blog.csdn.net/qiqi_skystar/article/details/52106859