• [JLOI2015] 骗我呢


    一、题目

    点此看题

    二、解法

    首先观察到一个条件 (0leq x_{i,j}leq m),结合 (x_{i,j}<x_{i,j+1}),我们可以用缺少的数来表示一行的状态。

    再考虑限制 (x_{i,j}<x_{i-1,j+1}),设 (a_i) 为第 (i) 行缺少的数,手玩可以发现这个限制可以转化成 (a_{i}+1geq a_{i+1}),显然可以设计一个 (dp) 来规划每一行缺少的数,设 (dp[i][j]) 表示第 (i) 行缺少的数是 (j),转移:

    [dp[i][j]=dp[i][j-1]+dp[i-1][j+1] ]

    优化可以考虑转移路径,我们把这个 (dp) 放到坐标系上:

    这个东西不是很好看啊,但是我们可以把行右移使得斜下的移动变成直下,然而第一列那个直下的转移会变成斜下,需要建虚点把这个东西化成直的,所以每一行就有 (m+2) 个点了:

    iwUwBd.png

    然后把这个东西放在我们熟悉的网格图上:

    iwUt1O.png

    那么问题变成了从 ((0,0)) 走到点 (T(n+m+1,n)),且不碰到直线 (A:y=x+1)(B:y=x-(m+2)) 的方案数,这么算的原因是你发现走到终点本身就包含了对最后一行状态求和的操作。

    乱走的方案数是 ({2n+m+1choose n}),考虑去除不合法的方案。我们可以把终点按照 (A) 翻折得到 (T_A'),那么从起点走到 (T_A') 就表示存在碰到 (A) 的路径方案,同理从起点走到 (T_B') 表示存在碰到 (B) 的路径方案,我们把他们减掉。

    然后发现 (AB,BA)(先按 (A) 翻折,再按 (B) 翻折,表示存在先碰到 (A),再碰到 (B) 的方案,反之亦然)这种要把他加回来,对于 (ABA,BAB) 要把它减回去(...)这就变成了一个容斥原理。

    ((x,y))(y=x+c) 翻折之后的坐标是 ((y-c,x+c)),然后组合数算一算就行了。

    三、总结

    考虑转移路径时,可以把形式简单的 (dp) 放在网格图上转化成网格图计数问题。

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int M = 3000005;
    const int MOD = 1e9+7;
    #define int long long
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,x,y,ans,fac[M],inv[M];
    void init(int n)
    {
    	inv[0]=inv[1]=fac[0]=1;
    	for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%MOD;
    	for(int i=2;i<=n;i++) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
    	for(int i=2;i<=n;i++) inv[i]=inv[i-1]*inv[i]%MOD;
    }
    int C(int n,int m)
    {
    	if(m<0 || n<m) return 0;
    	return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
    }
    void flip1(int &x,int &y) {swap(x,y);x--;y++;}
    void flip2(int &x,int &y) {swap(x,y);x+=m+2;y-=m+2;}
    int cal(int x,int y) {return C(x+y,y);}
    signed main()
    {
    	init(3e6);
    	n=read();m=read();x=n+m+1;y=n;
    	ans=cal(x,y);
    	while(x>=0 && y>=0)
    	{
    		flip1(x,y);ans-=cal(x,y);
    		flip2(x,y);ans+=cal(x,y);
    		ans=(ans%MOD+MOD)%MOD;
    	}
    	x=n+m+1;y=n;
    	while(x>=0 && y>=0)
    	{
    		flip2(x,y);ans-=cal(x,y);
    		flip1(x,y);ans+=cal(x,y);
    		ans=(ans%MOD+MOD)%MOD;
    	}
    	printf("%lld
    ",ans);
    }
    
  • 相关阅读:
    leveldb的搜索
    分布式存储bfs
    golang channel的行为
    支持rotate和大小限制的golang log库
    后台架构 一些需要注意的地方
    不要滥用面向对象,写出难以阅读和修改的代码
    goloader
    逻辑引擎、工作流、CMDB小感
    HTML5学习笔记4
    HTML5学习笔记3
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15105407.html
Copyright © 2020-2023  润新知