zoukankan      html  css  js  c++  java
  • 【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) ightarrow (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$ 的一个子串。

    $$egin{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;
    }
  • 相关阅读:
    AC自动机 HDOJ 2222 Keywords Search
    AC自动机 HDOJ 5384 Danganronpa
    贪心 HDOJ 5385 The Path
    区间DP UVA 10739 String to Palindrome
    区间DP UVA 10453 Make Palindrome
    素数专题
    判素数+找规律 BestCoder Round #51 (div.2) 1001 Zball in Tina Town
    DP专题
    贪心+模拟 ZOJ 3829 Known Notation
    概率DP ZOJ 3822 Domination
  • 原文地址:https://www.cnblogs.com/shxnb666/p/11626519.html
Copyright © 2011-2022 走看看