[JZOJ1901] 2010集训队出题光棱坦克

Posted jz-597

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[JZOJ1901] 2010集训队出题光棱坦克相关的知识,希望对你有一定的参考价值。

题目

题目大意

给你个平面上的一堆点,问序列\(p_i\)的个数。
满足\(y_p_i-1>y_p_i\)并且\(x_p_i\)\(x_p_i-1\)\(x_p_i-2\)之间。


正解

我不知道为什么我的树状数组打挂了……尽管不一定能AC,但是WA了……

这题的正解有很多,最为传奇的,则是彭大爷的神仙解法。
显然这是个DP,而他抛弃了按照\(y\)从大到小排序的传统做法,反而是以\(x\)从小到大排序。将\(p_i\)倒过来做。设\(f_i,0/1\)表示到\(i\)这个点,上一个点在左边或者右边的方案数。
DP的时候\(i\)从左到右扫过去,然后从右到左枚举\(j\),有两种转移:
如果\(y_j<y_i\),则从\(f_j,1\)转移到\(f_i,0\)
如果\(y_j>y_i\),则从\(f_i,0\)转移到\(f_j,1\)
这样的转移为什么是对的?实际上随便画个图就能理解了。
具体来说,在第一类转移的时候,很显然之前转移到\(f_j,1\)的是\(j\)\(i\)之间的状态;
在第二类转移的时候,很显然之前转移到\(f_i,0\)的是\(j\)\(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真是太鬼畜了……
彭大爷牛逼!!!
%%%

以上是关于[JZOJ1901] 2010集训队出题光棱坦克的主要内容,如果未能解决你的问题,请参考以下文章

jzoj19302010集训队出题期望彩色圆环

JZOJ 1981. 2011集训队出题Digit

JZOJ18992010集训队出题剪枝

JZOJ19142011集训队出题最短路

清橙 A1210. 光棱坦克

圆方树小结