标签:
题目链接: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