• 【JZOJ1901】【2010集训队出题】光棱坦克


    题目大意

    一个平面直角坐标系上,有(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;
    }
    
  • 相关阅读:
    11.枚举类.md
    10. Lambda表达式.md
    9.内部类
    8.抽象类、接口和多态.md
    7.final关键字.md
    jQuery学习笔记(5)-事件与事件对象
    Log4Net学习笔记(1)-完整的例子
    SqlServer知识点-操作xml
    NHibernate常见错误汇总(0)-持续更新中
    jQuery学习笔记(4)-设置元素的属性和样式
  • 原文地址:https://www.cnblogs.com/zjlcnblogs/p/11350172.html
Copyright © 2020-2023  润新知