题目
题目大意
给你个平面上的一堆点,问序列
p
i
{p_i}
pi的个数。
满足
y
p
i
−
1
>
y
p
i
y_{p_{i-1}}>y_{p_i}
ypi−1>ypi并且
x
p
i
x_{p_i}
xpi在
x
p
i
−
1
x_{p_i-1}
xpi−1和
x
p
i
−
2
x_{p_i-2}
xpi−2之间。
正解
我不知道为什么我的树状数组打挂了……尽管不一定能AC,但是WA了……
这题的正解有很多,最为传奇的,则是彭大爷的神仙解法。
显然这是个DP,而他抛弃了按照
y
y
y从大到小排序的传统做法,反而是以
x
x
x从小到大排序。将
p
i
{p_i}
pi倒过来做。设
f
i
,
0
/
1
f_{i,0/1}
fi,0/1表示到
i
i
i这个点,上一个点在左边或者右边的方案数。
DP的时候
i
i
i从左到右扫过去,然后从右到左枚举
j
j
j,有两种转移:
如果
y
j
<
y
i
y_j<y_i
yj<yi,则从
f
j
,
1
f_{j,1}
fj,1转移到
f
i
,
0
f_{i,0}
fi,0
如果
y
j
>
y
i
y_j>y_i
yj>yi,则从
f
i
,
0
f_{i,0}
fi,0转移到
f
j
,
1
f_{j,1}
fj,1
这样的转移为什么是对的?实际上随便画个图就能理解了。
具体来说,在第一类转移的时候,很显然之前转移到
f
j
,
1
f_{j,1}
fj,1的是
j
j
j和
i
i
i之间的状态;
在第二类转移的时候,很显然之前转移到
f
i
,
0
f_{i,0}
fi,0的是
j
j
j和
i
i
i之间的状态。
这样就保证了题目要求的性质。
代码
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 7010
inline int input(){
char ch=getchar();
while (ch<'0' || '9'<ch)
ch=getchar();
int x=0;
do{
x=x*10+ch-'0';
ch=getchar();
}
while ('0'<=ch && ch<='9');
return x;
}
int n,m,mo;
struct Node{
int x,y;
} d[N];
inline bool cmpd(const Node &a,const Node &b){return a.x<b.x;}
int f[N][2];
int main(){
n=input(),mo=input();
for (int i=1;i<=n;++i)
d[i]={input(),input()};
sort(d+1,d+n+1,cmpd);
for (int i=1;i<=n;++i){
f[i][0]=f[i][1]=1;
for (int j=i-1;j>=1;--j)
if (d[j].y<d[i].y)
(f[i][0]+=f[j][1])%=mo;
else
(f[j][1]+=f[i][0])%=mo;
}
long long ans=0;
for (int i=1;i<=n;++i)
ans+=f[i][0]+f[i][1];
printf("%lld\n",((ans-n)%mo+mo)%mo);
return 0;
}
总结
这样的DP真是太鬼畜了……
彭大爷牛逼!!!
%%%