luogu3431-POI

第一道传说中的二维偏序!

题目本身是一个很经典的模型,事实上只要把n和m的数据范围缩成1000左右,就是一个o(nm)的DP入门题。然而这道题的数据范围显然这样时间和空间都会爆炸。于是经过观察我们发现k很小,是一个可以接受的数据范围,所以我们从k入手,只考虑有人的那些点。易得当(i,j)的答案可被(x,y)更新需满足的条件为i>=x且j>=y。我们开一个结构体记录一下有人的点的坐标,把他们按照x排序一下,就能保证x的单增。然后我们考虑y的顺序。首先肯定是要把y离散化一下,然后我们定义f[i]表示按x排序后前i个点里选一定要选点i时的最优解,可得方程为f[i]=max(f[j])+a[i](1<=j<=i),其中a[i]表示第i个点的人数,于是我们只要快速的求出满足y小于等于当前点中的f最大的点就行了。这里我们用树状数组来维护。

事实上根本不用f数组,直接用一个变量记录即可。

贴出代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
long long f[1000000];
long long z[1000000];
long long lowbit(long long x)
{
return x&-x;
}
long long n,m,k;
inline long long ask(long long x)
{
long long r=0;
for(long long i=x;i>0;i-=lowbit(i))
r=max(r,f[i]);
return r;
}
inline void add(long long x,long long v)
{
for(long long i=x;i<=k;i+=lowbit(i))
f[i]=max(f[i],v);
}
struct point{
long long x,y;
long long num;
}p[1000000];
bool cmp(point a,point b)
{
if(a.x==b.x)return a.y<b.y;
return a.x<b.x;
}
int main()
{
scanf("%lld%lld%lld",&n,&m,&k);
for(long long i=1;i<=k;i++)
{
scanf("%lld%lld%lld",&p[i].x,&p[i].y,&p[i].num);
z[i]=p[i].y;
}
sort(z+1,z+k+1);
n=unique(z+1,z+1+k)-z-1;
for(long long i=1;i<=k;i++)
p[i].y=lower_bound(z+1,z+1+n,p[i].y)-z;
sort(p+1,p+1+k,cmp);
long long ans=0;
for(long long i=1;i<=k;i++)
{
long long x=ask(p[i].y)+p[i].num;
ans=max(ans,x);
add(p[i].y,x);
}
printf("%lld",ans);
return 0;
}

一开始WA了好几次,发现都是n和k弄混了。。。。都换过来就A了QAQ