光棱坦克
试题来源
2010中国国家集训队命题答辩
问题描述
一个平面直角坐标系上,有N个点,标号为1到N,其中第i个点的坐标为(x[i], y[i])。
求满足以下两个条件的点列{p[i]}的数目(假设{p[i]}的长度为M):
1) 对任意1 <= i < j <= M,必有y[p[i]] > y[p[j]];
2) 对任意3 <= i <= 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取模。
输入格式
第1行是两个由空格隔开的整数:N和Q。
第2行到第N+1行,每行有两个整数。其中的第i行的两个整数分别是x[i]和y[i]。
输出格式
输出文件只有一行,包含一个整数,表示序列{p[i]}的数目对Q取模的结果。
样例输入
4 100
2 2
3 1
1 4
4 3
样例输出
14
样例说明
一共4个点,位置如下:
如果M=4, 那么只有1种序列符合要求,如下图所示:
如果M = 3,那么有3种序列符合要求,如下图:
如果M = 2,那么有6种序列符合要求,如下图:
如果M = 1,也就是点列只包含一个点的情况。那么有4种序列。明显都符合要求。
所以一共就有1 + 3 + 6 + 4一共14种序列符合要求。
数据规模和约定
对于25%的数据,N <= 50;对于40%的数据,N <= 700;对于60%的数据,N <= 2000;对于70%的数据,N <= 4000;对于100%的数据,1 <= N <= 7000。
对于100%的数据,有1 <= Q <= 1000000000。
对于50%的数据,保证对任何的i,x[i]和y[i]是1到N之间的整数;对于100%的数据,保证对任何的i,x[i]和y[i]都是1到2000000000之间的整数。
对于100%的数据,保证有当i != j时,有x[i] != x[j]且y[i] != y[j]。
去年三月份和某dalao想了一下午做出来的题……
被这个dalao(就是友链里的第二个)拿去晚间划水(晚间水题选讲)讲掉了……
于是决定写一篇博客…..
毕竟是道很好的题……
思路:
观察样例,考虑将最终的方案按顺序连边,你会发现这极其像一个简笔画的龙卷风……
如果思考如何按
y
的顺序进行dp,你会发现貌似想不到什么东西,仅有的几个想法也极其难写。
(有dalao想到了方法欢迎打脸)
那么考虑从不那么自然的角度——按
考虑令
f[i][0/1]
代表以
i
为起点,且下一个节点在
那么可以得出一个很奇怪的转移:
从右到左枚举所有
j<i
,若
y[j]>y[i]
,
那么从
i
转移到
否则从
j
转移到
最难理解的其实是这个从
i
转移到
为什么这样转移?
如果在处理
i
点时,发现一个
注意,此时的
f[i][0]
已经保存了所有在
(j,i)
区间中的方案,从而保证了”长得像龙卷风”这一性质。
那么这就相当于把
i
接到
而这个被更新过的
f[j][1]
又可以用于更新其他满足
y[j]<y[i]
的
f[i][0]
,相当于把
j
接到了这样的
并且可以发现,此时的
i
的
于是这个dp方法便十分有理有据辣~
这个又短又快的
O(n2)
算法顶着清橙的老爷机跑出了
0.2s
的好成绩!
写下以下代码时作者还处于习惯性打括号时期~
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
struct po{
int x,y;
}p[7050];
int dp[7050][2];
int modd,n;
bool cmp(po a,po b){
return a.x<b.x;
}
int main(){
scanf("%d%d",&n,&modd);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&p[i].x,&p[i].y);
}
sort(p+1,p+n+1,cmp);
for(int i=1;i<=n;i++)
{
dp[i][0]=dp[i][1]=1;
for(int j=i-1;j>=1;j--)
{
if(p[j].y>p[i].y)
{
dp[j][1]+=dp[i][0];
if(dp[j][1]>modd)
dp[j][1]-=modd;
}
else
{
dp[i][0]+=dp[j][1];
if(dp[i][0]>modd)
dp[i][0]-=modd;
}
}
}
int ans=0;
for(int i=1;i<=n;i++){
ans+=dp[i][1];
if(ans>modd)
ans-=modd;
ans+=dp[i][0];
if(ans>modd)
ans-=modd;
}
ans-=n;
if(ans<0)
ans+=modd;
printf("%d\n",ans);
return 0;
}