HDOJ 5792 (2016多校联合训练 Training Contest 5) World is Exploding

来源:互联网 发布:韩国高考 知乎 编辑:程序博客网 时间:2024/06/05 14:01

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5792


题意,给我们n个数的序列A,问我们能够找到多少四个不同位置a,b,c,d的数满足:a≠b≠c≠d,1≤a<b≤n,1≤c<d≤n,Aa<Ab,Ac>Ad.

这题很明显可以用到容斥原理去处理,我们可以先找到所有的情况然后我们再去减去多余的部分即可,算总个数显然就是所有的上升序列个数*所有的下降序列的个数。仔细思考,我们多余的是哪一部分的内容,很好想到,多处的部分就是我们只取了三个点的部分,所以我们需要减去的部分就是只选取了三个点又满足了含有上升序列和下降序列的即可。

对于求解的方法,我们可以统计每一位上的左边比它小的个数和,这个我们可以用到树状数组进行实现,而统计左边比它大的个数我们可以直接用到左边的总个数减去比它小的数的个数以及和它相等的个数。

#include<cstdio>#include<cstring>#include<algorithm>#define ms(a,b) memset(a,b,sizeof(a))using namespace std;typedef long long LL;const int maxn=5e4;struct tnode{    int x,y;}p[maxn+5];int a[maxn+5],b[maxn+5];int lmin[maxn+5],rmin[maxn+5];int s1[maxn+5],s2[maxn+5];bool cmp(tnode aa,tnode bb){return aa.x<bb.x;}int query(int q[],int x){int ans=0;while(x>0)    {    ans+=q[x];    x-=(x&(-x));    }return ans;}void add(int q[],int x,int k){for(;x<=maxn;x+=(x&(-x)))b[x]+=k;}int main(){int n,i,j,k;LL ans,sum1,sum2;while(~scanf("%d",&n))    {    for(i=1;i<=n;i++)scanf("%d",&p[i].x),p[i].y=i;    if(n<4)        {        printf("0\n");        continue;        }    sort(p+1,p+1+n,cmp);    k = a[p[1].y] = 1;    for(i=2; i<=n; i++)        if(p[i].x == p[i-1].x) a[p[i].y] = k;        else a[p[i].y] = (++k);    for(ms(s1,0),i=1; i<=n; i++) s1[a[i]]++;    for(ms(b,0),i=1; i<=n; i++)        lmin[i] = query(b,a[i]-1),add(b,a[i],1);    for(ms(b,0),i=n; i>=1; i--)        rmin[i] = query(b,a[i]-1),add(b,a[i],1);      sum1 = sum2 = 0;    for(i=1; i<=n; i++) sum1 += lmin[i],sum2 += rmin[i];    ans = sum1*sum2;    for(ms(s2,0),i=1;i<=n;i++)        {        ans -= lmin[i]*rmin[i];        ans -= (i-1-lmin[i]-s2[a[i]])*(n-i-rmin[i]-(s1[a[i]]-s2[a[i]]-1));        ans -= rmin[i]*(n-i-rmin[i]-(s1[a[i]]-s2[a[i]]-1));        ans -= lmin[i]*(i-1-lmin[i]-s2[a[i]]);        s2[a[i]]++;        }    printf("%I64d\n",ans);    }return 0;}


0 0
原创粉丝点击