AtCoder Grand Contest 018&019
018E Sightseeing Plan
题目描述
解法
考虑枚举中转点 ,那么对应的路径数是: 到第一个矩形的方案数 到第二个矩形的方案数。
可以以 建立平面直角坐标系,那么可以差分成四个矩形,问题变成了在矩形内停下的方案数:
设矩形右上角的坐标是 ,那么显然方案数是
应用组合意义来化简,考虑组合意义是 个球依次添加了 个球,然后要从中选出 个球。再新增一个球表示添加到哪里了,可以转化成 个球中选择 个球,即
再次应用同样的组合意义,我们可以得到
发现这就是 到 的方案数,所以我们可以把矩形拆分成四个点,计算点到点的方案数。
回到原问题,我们成功地把问题转化成了:固定起点和终点,一条路径的贡献是其在给定矩形内的长度 ,求所有路径的贡献和。枚举中转点太慢了,我们只需要知道一条路径在哪里进入矩形,在哪里离开矩形,就可以知道在矩形内的长度。
发现这两部分又可以拆分计算,因为长度可以看成曼哈顿距离,所以进入和出去的路径都贡献 横纵坐标之和 次即可。
枚举进入矩形的点 ,那么有这样的负贡献:起点到 的方案数 到终点的方案数
枚举离开矩形的点 ,那么有这样的正贡献:起点到 的方案数 到终点的方案数
时间复杂度
#include <cstdio>
const int M = 2000005;
const int MOD = 1e9+7;
#define int long long
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int x[7],y[7],x3,x4,y3,y4,ans,fac[M],inv[M];
struct node{int x,y,f;}s[10];
void init(int n)
{
fac[0]=inv[0]=inv[1]=1;
for(int i=2;i<=n;i++) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=n;i++) inv[i]=inv[i-1]*inv[i]%MOD;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%MOD;
}
int Abs(int x) {return x>0?x:-x;}
int C(int n,int m)
{
if(n<m || m<0) return 0;
return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
int G(int x1,int y1,int x2,int y2)
{
int x=Abs(x2-x1),y=Abs(y2-y1);
return C(x+y,x);
}
void add(int &x,int y) {x=(x+y%MOD+MOD)%MOD;}
int zxy(int x1,int y1,int x2,int y2)
{
int r=0;
for(int x=x3;x<=x4;x++)
{
add(r,G(x1,y1,x,y4)*G(x,y4+1,x2,y2)%MOD*(x+y4+1));
add(r,-G(x1,y1,x,y3-1)*G(x,y3,x2,y2)%MOD*(x+y3));
}
for(int y=y3;y<=y4;y++)
{
add(r,G(x1,y1,x4,y)*G(x4+1,y,x2,y2)%MOD*(x4+y+1));
add(r,-G(x1,y1,x3-1,y)*G(x3,y,x2,y2)%MOD*(x3+y));
}
return r;
}
signed main()
{
init(2000000);
for(int i=1;i<=6;i++) x[i]=read();
for(int i=1;i<=6;i++) y[i]=read();
x3=x[3];y3=y[3];x4=x[4];y4=y[4];
s[1]=node{x[1]-1,y[1]-1,1};
s[2]=node{x[1]-1,y[2],-1};
s[3]=node{x[2],y[1]-1,-1};
s[4]=node{x[2],y[2],1};
//
s[5]=node{x[5],y[5],1};
s[6]=node{x[6]+1,y[5],-1};
s[7]=node{x[5],y[6]+1,-1};
s[8]=node{x[6]+1,y[6]+1,1};
//
for(int a=1;a<=4;a++) for(int b=5;b<=8;b++)
add(ans,s[a].f*s[b].f*
zxy(s[a].x,s[a].y,s[b].x,s[b].y));
printf("%lld\n",ans);
}
019F Yes or No
题目描述
解法
考虑一个朴素的 ,设 表示有 个问题是 YES
,有 个问题是 NO
,最优策略一定是猜测较多的那一个,但是答案可以看成是随机的,所以有转移:
显然这东西是具有对称性的:,考虑它的组合意义,就是在 的路径上行走,从起点 到终点 ,每次可以向下或者向右走一步,每种路径有其对应的权值。
考虑对每种路径分别统计权值,有一个关键的 是:不妨设 ,如果路径始终在 的上方,那么贡献一定是 ,这是因为最优策略总是选 YES
那么对于穿过 的路径,根据前文所提到的对称性,我们把它翻折上去。但是这样会漏算一小部分的贡献,因为在 的这一步一定不会被我们统计任何贡献,而实际上它是有一个 的贡献的,要把它算上去。
那么问题就转化成了统计 被穿过的总次数,被穿过一次就多 的贡献,所以答案是:
时间复杂度
#include <cstdio>
#include <iostream>
using namespace std;
const int M = 1000005;
const int MOD = 998244353;
#define int long long
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,ans,fac[M],inv[M];
void init(int n)
{
fac[0]=inv[0]=inv[1]=1;
for(int i=2;i<=n;i++) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=n;i++) inv[i]=inv[i-1]*inv[i]%MOD;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%MOD;
}
void add(int &x,int y) {x=(x+y)%MOD;}
int C(int n,int m)
{
if(n<m || m<0) return 0;
return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
int qkpow(int a,int b)
{
int r=1;
while(b>0)
{
if(b&1) r=r*a%MOD;
a=a*a%MOD;
b>>=1;
}
return r;
}
signed main()
{
n=read();m=read();init(n+m);
if(n<m) swap(n,m);
for(int i=1;i<=m;i++)
add(ans,C(m-i+n-i,m-i)*C(2*i,i));
ans=ans*qkpow(2*C(n+m,n)%MOD,MOD-2)%MOD;
printf("%lld\n",(ans+n)%MOD);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
2021-06-02 NOMURA Programming Contest 2021
2021-06-02 Deltix Round, Spring 2021