【JLOI2015】骗我呢

题意简述:

求原点到点$P(n+m+1,n)$,并且不能经过(或跨越)直线$A:y=x+1$和$B:y=x-(m+2)$的路径条数。

题解:

这道题应该可以算是格路计算问题的巅峰了吧。

首先,我们把$P$点沿$A$进行翻折,记为$P'$,从原点到$P'$的路径条数就是“从原点到$P$,一定经过(或跨越)直线$A$的路径条数”

注意我们不知道是什么时候经过的,也不知道前后还有没有别的经过事件,因为我们只考虑了直线$A$。

我们把$P'$沿$B$翻折,记为$P''$,考虑从原点到$P''$的路径条数是什么意思:

由上面一个问题,我们很自然的猜想,“是不是从原点到$P$,一定经过(或跨越)直线$A$和$B$的路径条数呢?”

但是我们很快可以发现这个猜想是错误的,因为如果是这样的话,由对称性可知,先沿$A$折,再沿$B$折,和先沿$B$折,再沿$A$折,算出来的东西是一样的。

进一步的,即$A$和$B$关于直线$y=x$对称,

但其实,并没有保证这个条件。

那我们继续很自然的猜想,“是不是从原点到$P$,先经过$B$再经过$A$的路径条数呢”(注意这里我可能没有讲清楚,是保证有两次经过,并且有先$A$再$B$),

发现就是这样,因为我们可以把第一次经过$B$的部分沿$B$翻折,从第一个交点处到$P'$,一定要跨越$A$,所以是先$B$后$A$。

我们发现还是可以反复经过$B$,再经过$A$的。那么我们就把多次连续的经过一条直线记做一个事件,即把经过直线状态的改变看做一个事件。

那所有的不合法方案就是形如$A$,$B$,$AB$,$BA$,$ABA...$,共同点是$AB$交替出现,不知道先$A$还是先$B$,也不知道什么时候结束。

我们可以看成有一个文本检索器,每次可以搜一个子序列,查找文本中出现了此子序列的子段个数(更严谨的,查找文本中出现了此子序列的子段加权的个数和),怎么容斥的把所有不合法方案算出来。

先减去$A$开头的,再减去$B$开头的就可以了。

如何算$A$开头的?我们先沿$A$翻折,查$A$,算出来是多的,因为还有$B$开头且含$A$的,再把这一部分减去,即沿$B$翻折,查$BA$,减去$BA$的,但是会有多减的,因为还有$ABA$开头的,就再加上,发现加多了,因为还有开头$BABA$的......下一次的永远比上一次少,也就是它肯定会少到$0$,就在那里停止就可以了。

算$B$开头的同理。

 

## update:2020 - 5 - 12 

听神仙 `aysn` 讲了这个问题,想到自己之前做过,用现在的方法重新推一遍。

重新写一下这个问题,即从 $(0,0) \rightarrow (n,n)$,不能跨越(但是可以经过)直线 $y=x$ 和 $y=x-m$,求方案数。

对于 $(0,0)$ 到 $(n,n)$ 的一条路径 $P$,定义一个字符串 $S$,设它在行进过程中,如果经过了直线 $l_1$,就在 $S$ 后面加一个 $1$,如果它经过了 $l_2$,就在 $S$ 后面加一个 $2$,这样形成的字符串 $S$,我们规定它为 $w(P)$,但是我们发现这样没法做,所以把这个字符串连续的一段 $1$ 和 $2$ 缩成一个 $1/2$。设 $P'$ 为 $P$ 的一个子串。

$$\begin{align*}ans&=\sum_P [w(P)=""]\\&=\sum_P \sum_{P' \subs P} (-1)^{|P|}=\sum_{P'}(-1)^{|P'|}\sum_{P}|P'\subset P| \end{align*}$$

对于 $P'\ subset P$ 的所有路径 $P$,我们只需要按照 $P'$ 翻着 $(n,n)$,得到的点 $(x,y)(x+y=n+n)$,即为符合条件的 $P$ 的个数。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define mod 1000000007
#define N 3000300
#define LL long long 
#define ri register int
void add(int &x,int y) {
    x+=y;
    if (x>=mod) x-=mod;
}
int mul(int a,int b) {
    LL c=a*1LL*b;
    return c%mod;
}
int n,m,inv[N],jc[N],jv[N],maxn,ans;
int c(int x,int y) {
    if (x<0 || y<0) return 0;
    return mul(mul(jc[x+y],jv[x]),jv[y]);
}
void flip1(int &x,int &y) {
    swap(x,y);
    x-=1; y+=1;
}
void flip2(int &x,int &y) {
    swap(x,y);
    x+=m+2; y-=m+2;
}
int main() {
    cin>>n>>m;
    inv[0]=inv[1]=jc[0]=jv[0]=1;
    maxn=max(n,m)*3+1;
    for (ri i=2;i<=maxn;i++) inv[i]=mul(inv[mod%i],(mod-mod/i));
    for (ri i=1;i<=maxn;i++) jc[i]=mul(jc[i-1],i);
    for (ri i=1;i<=maxn;i++) jv[i]=mul(jv[i-1],inv[i]);
    int x=n+m+1,y=n; ans=c(x,y);
    while (x>=0 && y>=0) {
      flip1(x,y);
      add(ans,mod-c(x,y));
      flip2(x,y);
      add(ans,c(x,y));
  }
  x=n+m+1,y=n;
  while (x>=0 && y>=0) {
      flip2(x,y);
      add(ans,mod-c(x,y));
      flip1(x,y);
      add(ans,c(x,y));
  }
  cout<<ans<<endl;
  return 0;
}

 

posted @ 2019-10-06 09:39  HellPix  阅读(216)  评论(0编辑  收藏  举报