POJ 3378 Crazy Thairs dp+树状数组

题目:http://poj.org/problem?id=3378

题意:求满足下列要求的5元组的个数

1. 1 ≤ i < j < k < l < m  N
2. Ai < Aj < Ak < Al < Am

学习了一种机智的做法,在范围超longlong不大的时候可以使用2个longlong来避免写高精度

首先我们对数据进行离散化

设dp[i][j]为以 a[j] 结尾的上升 i 序列 1<=i<=5

dp[i][j]=sum{dp[i-1][k]}  a[k]<a[j] 1<=k<j

然后我们可以用树状数组来维护sum

add(i,a[j],sum(i-1,a[j]-1))

因为最后一维是答案且不需要查询,而且只有这一维的大小会爆longlong,所以我们可以用2个longlong来存放答案

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
const int N=5e4+5;
const ll base=1e15;
struct p
{
    int x,y;
    bool operator <(const p &t) const
    {
        return x<t.x;
    }
};
p tem[N];
int a[N];
ll s[5][N],ans1,ans2;
void add(int k,int i,ll x)
{
    while(i<N)
    {
        s[k][i]+=x;
        i+=i&-i;
    }
}
ll sum(int k,int i)
{
    ll t=0;
    while(i>0)
    {
        t+=s[k][i];
        i-=i&-i;
    }
    return t;
}
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&tem[i].x);
            tem[i].y=i;
        }
        sort(tem+1,tem+1+n);
        memset(s,0,sizeof(s));
        a[tem[1].y]=1;
        int cnt=1;
        for(int i=2;i<=n;i++)
            if (tem[i].x==tem[i-1].x) a[tem[i].y]=cnt;
            else a[tem[i].y]=++cnt;
        ans1=ans2=0;
        for(int i=1;i<=n;i++)
        {
            add(1,a[i],1);
            for(int j=2;j<=4;j++)
                add(j,a[i],sum(j-1,a[i]-1));
            ans1+=sum(4,a[i]-1);
            ans2+=ans1/base;
            ans1%=base;
        }
        if (ans2) printf("%lld",ans2);
        printf("%lld
",ans1);
    }
    return 0;
}

  

原文地址:https://www.cnblogs.com/bk-201/p/7453714.html