zoukankan      html  css  js  c++  java
  • [AGC018E] Sightseeing Plan

    问题描述

    一个人在网格图上旅行,他可以从矩形 ((X_1,Y_1)-(X_2,Y_2)) 中任意一点 (S) 开始,再在矩形 ((X_3,Y_3)-(X_4,Y_4)) 中任意一点 (P) 午休,最后在矩形 ((X_5,Y_5)-(X_6,Y_6)) 中任意一点 (T) 结束旅行。

    如果旅行者当前在 ((x,y)) ,那么他下一步只能移动到 ((x,y+1))((x+1,y))

    只要点 (S,P,T) 不同或旅行经过的点集不同即为不同的方案,求有多少种不同的行走方案,答案对 (10^9+7) 取膜。

    解析

    我们从问题的简单形式来考虑。

    点到点的方案数

    比较显然的结论,从点 ((x_1,y_1))((x_2,y_2)) 的方案数为

    [{|x_2-x_1|+|y_2-y_1|}choose|x_2-x_1| ]

    点到矩形的方案数

    设点为 ((0,0)),矩形的左下角和右上角分别为 ((x_2,y_2),(x_3,y_3))

    首先有一个结论: 记从 ((0,0))((x,y)) 的路径方案数为 (C(x,y))。可以表示成:

    [C(x,y)=sum_{i=0}^{y}C(i,y-1) ]

    即先走到第 (y-1) 行,然后只有唯一的方法走到 ((x,y))

    然后考虑如何计算点到矩形的方案。到一个矩形的方案即到矩形中每一个点的方案和,考虑点 ((x_3+1,y_3+1))(C(x_3+1,y_3+1)) 相当于 (C(0,y))(C(x,y)) 之和加上 (C(x+1,y)) 。后者可以用类似的方式展开。以此类推,最后再减去多出来的 (C(x_3+1,y_2)),我们就得到了 ((0,0)) 到矩形 (((0,y_2),(x_3,y_3))) 的方案。然后用同样的方法计算到矩形 (((0,y_2),(x_2-1,y_3))) 的方案然后相减即可得到答案:

    [C(x_3+1,y_3+1)-C(x_3+1,y_2)-C(x_2,y_3+1)+C(x_2,y_2) ]

    矩形到矩形的方案

    首先枚举第一个矩形中的点,问题转化为求每个点到另一个矩形的方案。由于点到矩形的方案数可以转化为点到四个关键点的方案数,实际上我们相当于要求第二个矩形的四个关键点到第一个矩形的方案数。通过类似的方法,我们可以分别枚举两个矩形的关键点,处理好对答案贡献的正负号就可以了。

    回到问题

    原问题相当于在两个矩形中间添加了一个矩形,要求我们在第二个矩形中选择一个点,然后经过这个选中的点。即求第一个矩形到第三个矩形的所有路径中经过第二个矩形的长度之和。实际上我们只要知道进入第二个矩形的点和离开第二个矩形的点,长度就是这两个点的曼哈顿距离加一。因此,我们对边界上每一个点计算贡献(曼哈顿距离公式中对应部分乘上方案数),要保证一定是从这个点离开或者进入。

    代码

    #include <iostream>
    #include <cstdio>
    #define int long long
    #define N 1000002
    #define T 2000000
    using namespace std;
    const int mod=1000000007;
    struct node{
    	int x,y,op;
    }a[10],b[10];
    int x[10],y[10],i,j,fac[2*N],inv[2*N],ans;
    int poww(int a,int b)
    {
    	int ans=1,base=a;
    	while(b){
    		if(b&1) ans=ans*base%mod;
    		base=base*base%mod;
    		b>>=1;
    	}
    	return ans;
    }
    int C(int n,int m)
    {
    	return fac[n]*inv[m]%mod*inv[n-m]%mod;
    }
    int get(int x1,int y1,int x2,int y2)
    {
    	return C(x2-x1+y2-y1,x2-x1);
    }
    int cal(int x1,int y1,int x2,int y2)
    {
    	int x3=x[3],y3=y[3],x4=x[4],y4=y[4],ans=0;
    	for(int i=x3;i<=x4;i++){
    		ans=(ans+get(x1,y1,i,y4)*get(i,y4+1,x2,y2)%mod*(i+y4+1)%mod)%mod;
    		ans=(ans-get(x1,y1,i,y3-1)*get(i,y3,x2,y2)%mod*(i+y3)%mod+mod)%mod;
    	}
    	for(int i=y3;i<=y4;i++){
    		ans=(ans+get(x1,y1,x4,i)*get(x4+1,i,x2,y2)%mod*(i+x4+1)%mod)%mod;
    		ans=(ans-get(x1,y1,x3-1,i)*get(x3,i,x2,y2)%mod*(i+x3)%mod+mod)%mod;
    	}
    	return ans;
    }
    signed main()
    {
    	for(i=1;i<=6;i++) scanf("%lld",&x[i]);
    	for(i=1;i<=6;i++) scanf("%lld",&y[i]);
    	for(i=fac[0]=1;i<=T;i++) fac[i]=fac[i-1]*i%mod;
    	inv[T]=poww(fac[T],mod-2);
    	for(i=T-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod;
    	a[1]=(node){x[6]+1,y[6]+1,1};
    	a[2]=(node){x[5],y[6]+1,-1};
    	a[3]=(node){x[6]+1,y[5],-1};
    	a[4]=(node){x[5],y[5],1};
    	b[1]=(node){x[1]-1,y[1]-1,1};
    	b[2]=(node){x[2],y[1]-1,-1};
    	b[3]=(node){x[1]-1,y[2],-1};
    	b[4]=(node){x[2],y[2],1};
    	for(i=1;i<=4;i++){
    		for(j=1;j<=4;j++){
    			int res=cal(b[i].x,b[i].y,a[j].x,a[j].y);
    			if(b[i].op!=a[j].op) ans=(ans-res+mod)%mod;
    			else ans=(ans+res)%mod;
    		}
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    C#在与java对接时候的UrlEncode的坑
    sql server 删除大量数据的一次坑爹之旅
    js实现黑客帝国文字下落效果
    第一个SignalR案例
    简单的放天灯动画
    计量单位符号的书写规范【转】
    阿里云OSS搭建移动应用直传服务的.Net C#示例
    UWP Windows10开发更新磁贴和动态更新磁贴
    UWP Windows10开发获取设备位置(经纬度)
    Asp.Net识别手机访问
  • 原文地址:https://www.cnblogs.com/LSlzf/p/13586115.html
Copyright © 2011-2022 走看看