• [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
    ",((ans-n)%mo+mo)%mo);
    	return 0;
    }
    

    总结

    这样的DP真是太鬼畜了……
    彭大爷牛逼!!!
    %%%

  • 相关阅读:
    编写JS代码的“use strict”严格模式及代码压缩知识
    开发网站要从用户的角度出发!
    你好,世界
    JavaScript的几种函数的结构形式
    JavaScript功能检测技术和函数构造
    android打造万能的适配器
    C语言第二次博客作业分支结构
    C语言第三次博客作业单层循环结构
    C语言第一次博客作业——输入输出格式
    C语言第四次博客作业嵌套循环
  • 原文地址:https://www.cnblogs.com/jz-597/p/11423155.html
Copyright © 2020-2023  润新知