题目大意
一个平面直角坐标系上,有(N)个点,标号为(1)到(N),其中第i个点的坐标为((x[i],y[i]))。
求满足以下两个条件的点列({p[i]})的数目(假设({p[i]})的长度为(M)):
- 对任意(1 leq i < j leq M),必有(y[p[i]] > y[p[j]]);
- 对任意(3 leq i leq M),必有(x[p[i-1]] < x[p[i]] < x[p[i-2]])或者(x[p[i-2]] < x[p[i]] < x[p[i-1]])。
求满足条件的非空序列({p[i]})的数目,结果对一个整数(Q)取模。
(nleq 7000)
分析
设(f_{i,0/1}),表示(i)往左下/右下的方案数,转移的顺序成了问题。如果我们将点按照(x)排序,对于一个(i),倒序从(i-1)到(1)枚举(j),如果(y_j>y_i),就把(f_{i,0})转移到(f_{j,1}),否则将(f_{j,1})转移到(f_{i,0}),可以发现这样转移,只会从(y)较小的转移到较大的,并且(i)转移至(j)时,(i)对应的上一个点(k)一定在(i,j)中间,因此所有的转移都是合法的。这是一种极为巧妙的转移方式。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 7007;
int n, q, ans, f[N][2];
struct note { int x, y; } a[N];
int cmp(note p, note q) { return p.x < q.x; }
int main()
{
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; ++i) scanf("%d%d", &a[i].x, &a[i].y);
sort(a + 1, a + n + 1, cmp);
for (int i = 1; i <= n; ++i)
for (int j = i - 1; j >= 1; --j)
if (a[j].y > a[i].y) f[j][1] = (f[j][1] + f[i][0] + 1) % q;
else f[i][0] = (f[i][0] + f[j][1] + 1) % q;
for (int i = 1; i <= n; i++) ans = (ans + f[i][0] + f[i][1]) % q;
ans = (ans + n) % q;
printf("%d
", ans);
return 0;
}