把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷3266】[JLOI2015] 骗我呢(坐标系走路神仙题)

点此看题面

  • 有一张\(n\times m\)的网格图,每个位置只能填入\([0,m]\)
  • 要求满足每个位置\((i,j)\)\((i,j+1)\)\((i-1,j+1)\)都小,求方案数。
  • \(n,m\le10^6\)

动态规划

首先发现总共只有\(m+1\)种数,而每一行都要填\(m\)个递增的数,因此一行的状态实际上只需要考虑哪个数没有即可。

即,设\(f_{i,j}\)表示第\(i\)行没有的数是\(j\)的方案数。

考虑转移,画画图发现\(k\le j+1\)\(f_{i-1,k}\)都是可以转移的,而更大的\(k\)就不满足条件了。

因此得出转移方程:

\[f_{i,j}=\sum_{k=0}^{j+1}f_{i-1,k}=f_{i,j-1}+f_{i-1,j+1} \]

坐标系走路的转化

把转移表示到图上去:(图画得很丑,但我尽力了。。。)

发现斜着的转移很难表示,所以我们先把它掰直,变成:

那么也就相当于我们不能触碰到下图中的两条蓝线\(A\)\(B\)

容斥

(画不动图了,而且估计我画得这么丑也没人看,因此就不放了)

经典容斥问题。

以第一条线\(A\)为例,我们找到终点关于它的对称点,那么抵达对称点的方案等同于以\(A\)结尾或以\(AB\)结尾的非法路径。同理以\(B\)为例,抵达关于它的对称点的方案等同于以\(B\)结尾或以\(BA\)结尾的非法路径。

但它们之间的非法路径肯定存在包含关系,因为以\(A\)结尾也就包含了以\(BA\)结尾,因此需要容斥。

具体地,依旧以第一条线\(A\)为例,我们找到终点关于它的对称点,用答案减去这一部分方案数,然后找到这个对称点关于第二条线\(B\)的对称点,给答案加上这一部分方案数,然后再回去给当前对称点找到第一条线的对称点减去方案数,以此类推,直至越界(某一维小于\(0\))。第二条线则反之。

显然终点坐标为\((n+m+1,n)\)。而关于对称点,有解析式\(A:y=x+1,B:y=x-m-2\),所以\((x,y)\)关于\(a,b\)的对称点分别为\((y-1,x+1),(y+m+2,x-m-2)\)

代码实现很简单,但思维难度真的高。

代码:\(O(n)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000000
#define X 1000000007
using namespace std;
int n,m;I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
int Fac[3*N+5],IFac[3*N+5];I void InitFac(CI S)
{
	RI i;for(Fac[0]=i=1;i<=S;++i) Fac[i]=1LL*Fac[i-1]*i%X;//预处理阶乘
	for(IFac[i=S]=QP(Fac[S],X-2);i;--i) IFac[i-1]=1LL*IFac[i]*i%X;//预处理阶乘逆元
}
I void A(int& x,int& y) {RI a=y-1,b=x+1;x=a,y=b;}//把点按A翻转
I void B(int& x,int& y) {RI a=y+m+2,b=x-m-2;x=a,y=b;}//把点按B翻转
int main()
{
	#define Calc(x,y) ((x)>=0&&(y)>=0?1LL*Fac[(x)+(y)]*IFac[x]%X*IFac[y]%X:0)//到达(x,y)的方案数,就是C(x+y,x)
	scanf("%d%d",&n,&m),InitFac(3*max(n,m)+1);RI x,y,t=Calc(n+m+1,n);
	x=n+m+1,y=n;W(x>=0&&y>=0) A(x,y),t=(t-Calc(x,y)+X)%X,x>=0&&y>=0&&(B(x,y),t=(t+Calc(x,y))%X);//对于A的容斥
	x=n+m+1,y=n;W(x>=0&&y>=0) B(x,y),t=(t-Calc(x,y)+X)%X,x>=0&&y>=0&&(A(x,y),t=(t+Calc(x,y))%X);//对于B的容斥
	return printf("%d\n",t),0;
}
posted @ 2021-03-29 19:16  TheLostWeak  阅读(61)  评论(0编辑  收藏  举报