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;
    }
  • 相关阅读:
    RE
    【LeetCode】198. House Robber
    【LeetCode】053. Maximum Subarray
    【LeetCode】152. Maximum Product Subarray
    【LeetCode】238.Product of Array Except Self
    【LeetCode】042 Trapping Rain Water
    【LeetCode】011 Container With Most Water
    【LeetCode】004. Median of Two Sorted Arrays
    【LeetCode】454 4Sum II
    【LeetCode】259 3Sum Smaller
  • 原文地址:https://www.cnblogs.com/shxnb666/p/11626519.html
Copyright © 2011-2022 走看看