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\n",ans1);
}
return 0;
}

浙公网安备 33010602011771号