zoukankan      html  css  js  c++  java
  • AtCoder Grand Contest 018 E Sightseeing Plan

    题意:

    给定三个矩形,选定三个点,答案加上第一个点出发经过第二个点在第三个点结束的方案数,只能往右或往下走。

     

     

    折腾了我半个多下午的题。

    设三个矩形为$A,B,C$
    一个思路是枚举$B$的那个点$s(x,y)$,求出$s$到$A$中所有点的方案数的和乘上$x$到$B$中所有点的方案数的和,复杂度爆炸。

    $s$到$A$中所有点的方案数的和等于$$sum_{i=x1}^{x2}sum_{j=y1}^{y2} C_{x-i+x-j}^{x-i}$$

    用二维前缀和的方法容斥一下,变成了求(顺便化简)$$sum_{i=0}^{a}sum_{j=0}^{b} C_{i+j}^{i}=C_{a+b+1}^{a+1}-1$$
    复杂度变成了$O(N^2)$
    然后发现那个化简完的组合数其实相当于$s$到一个固定的点(不随$s$改变)的方案数,相当于固定了起点,可以用同样的方法固定终点。
    现在每条起点到终点的路径都有一个权值,相当于经过的$B$中的点的个数,答案就是所有路径权值和。
    再把$B$容斥一下,让矩形的左上角为起点,这样就可以枚举走出矩形后的第一个点求权值和了。
    复杂度$O(N*64)$

     

    #include<cstdio>
    #include<iostream>
    #define ll long long
    #define N 2000010
    using namespace std;
    const int p = 1000000007;
    int jie[N],ni[N];
    const int inf = 2000005;
    int c(int m,int n)
    {
        return 1LL*jie[n]*ni[m]%p*ni[n-m]%p;
    }
    ll ans;
    int ss(int x1,int y1,int x2,int y2,int x3,int y3)
    {
        int as=0;
        for(int i=y1;i<=y2;i++)
        {
            (as+=1LL*c(x3-x2-1,x3-x2-1+y3-i)*c(x2-x1,x2-x1+i-y1)%p*(x2-x1+1+i-y1)%p)%=p;
        }
        for(int i=x1;i<=x2;i++)
        {
            (as+=1LL*c(y3-y2-1,y3-y2-1+x3-i)*c(y2-y1,y2-y1+i-x1)%p*(y2-y1+1+i-x1)%p)%=p;
        }
        return as;
    }
    int x1,x2,x3,x4,x5,x6;
    int y1,y2,y3,y4,y5,y6;
    int calc(int a1,int b1,int a2,int b2)
    {
        int as=0;
        as+=ss(a1,b1,x4,y4,a2,b2);
        as+=ss(a1,b1,x3-1,y3-1,a2,b2);as%=p;
        as-=ss(a1,b1,x3-1,y4,a2,b2);
        as-=ss(a1,b1,x4,y3-1,a2,b2);
        return (as%p+p)%p;
    }
    int solve(int x,int y)
    {
        int as=0;
        as+=calc(x,y,x6+1,y6+1);as+=calc(x,y,x5,y5);as%=p;
        as-=calc(x,y,x6+1,y5);as-=calc(x,y,x5,y6+1);
        return (as%p+p)%p;
    }
    int main()
    {
        jie[0]=ni[0]=ni[1]=1;
        for(int i=1;i<=inf;i++)jie[i]=1LL*jie[i-1]*i%p;
        for(int i=2;i<=inf;i++)ni[i]=1LL*(p-p/i)*ni[p%i]%p;
        for(int i=2;i<=inf;i++)ni[i]=1LL*ni[i-1]*ni[i]%p;
        scanf("%d%d%d%d%d%d",&x1,&x2,&x3,&x4,&x5,&x6);
        scanf("%d%d%d%d%d%d",&y1,&y2,&y3,&y4,&y5,&y6);
        ans+=solve(x1-1,y1-1);
        ans+=solve(x2,y2);
        ans-=solve(x1-1,y2);
        ans-=solve(x2,y1-1);
        ans=(ans%p+p)%p;
        printf("%lld
    ",ans);
        return 0;
    }
    

      

     

  • 相关阅读:
    Go语言十六进制转十进制
    Go语言中底层数组和切片的关系以及数组扩容规则
    Golang超时机制--2秒内某个函数没被调用就认为超时
    约瑟夫环问题(猴子选大王)
    冒泡排序优化
    斐波那契数列
    Linux下使用acme.sh (Let's Encrypt) 配置https 免费证书
    git 本地分支指定对应的远程分支
    Git分支开发 -- 利用git pull命令将远程指定仓库的分支拉取到本地
    phpStorm 之 本地开发,Linux上跑项目(连接远端服务器开发)
  • 原文地址:https://www.cnblogs.com/ezyzy/p/7891607.html
Copyright © 2011-2022 走看看