AtCoder Grand Contest 051
链接
B. Bowling
题解
明哥题。题解给了一个 rand 的做法。明哥直接硬构造了一个分形。
3
33
000
1002
11022
其中 分别递归构造这个结构。
对于 层的构造,可以做到 的比例,消耗 的节点。
取 ,大概可以做到 倍左右。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=100010;
int x[N],y[N],tt;
void solve(int x0,int y0,int d,int n)
{
if(d==0){++tt,x[tt]=x0,y[tt]=y0;return;}
n/=3;solve(x0,y0,d-1,n),solve(x0+n*2,y0,d-1,n),solve(x0,y0-n*2,d-1,n);
}
int main()
{
solve(0,1e9,10,14348907);
printf("%d\n",tt);
for(int i=1;i<=tt;i++) printf("%d %d\n",x[i],y[i]);
return 0;
}
C. Flipper
题解
首先这类题有一个经典套路,考虑贪心以黑点为右下角往左往上推,可以证明无论怎么推最终得到的矩形是相同的。此时只有第一行和前两列可能存在黑点。
容易发现,排除平凡的全白点情况,对于某一行的黑点, 表示需要匹配列标号 的列黑点, 表示匹配 , 匹配 。
这样可以把整个矩形看做一个二分图,左右各有 类点 。相同类点可以直接匹配,同时左侧 可以花 代价匹配右侧 ,同理 匹配 , 匹配 。最后每个同侧的同类点可以内部花 代价两两匹配。
容易发现,如果同时存在 匹配了 , 匹配了 ,那么此时直接匹配 更优。所以最多只有一个数采取匹配两个右侧不同类点。
考虑枚举这类匹配的次数,剩下的一定先贪心异侧匹配,然后再同侧匹配。特别的,如果某类点两侧奇偶性不同,匹配直接不合法。
复杂度 。瓶颈在离散化。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
const int N=100010;
map<int,int>tx,ty;
int a[3],b[3];
int calc(){for(int i=0;i<3;i++) if((a[i]&1)!=(b[i]&1)) return 1e9;int r=0;for(int i=0;i<3;i++) r+=max(a[i],b[i]);return r;}
int main()
{
int n;scanf("%d",&n);
for(int i=1,x,y;i<=n;i++)
{
scanf("%d%d",&x,&y);
tx[x]^=3-y%3;
ty[y]^=1;
}
for(auto [u,v]:tx) if(v!=0) a[3-v]++;
for(auto [u,v]:ty) if(v==1) b[u%3]++;
// for(int i=0;i<3;i++) cerr<<a[i]<<" ";cerr<<endl;
// for(int i=0;i<3;i++) cerr<<b[i]<<" ";cerr<<endl;
int ans=1e9;
for(int x=0;x<3;x++)
{
for(int i=0;i<=a[x]+1 && i<=b[(x+1)%3] && i<=b[(x+2)%3];i++)
{
a[x]-=i,b[(x+1)%3]-=i,b[(x+2)%3]-=i;
ans=min(ans,calc()+i*2);
a[x]+=i,b[(x+1)%3]+=i,b[(x+2)%3]+=i;
}
}
printf("%d\n",ans);
return 0;
}
D. C4
题解
考虑枚举 边 的经过次数,这样可以推出每条边每个方向的经过次数。这样就变成了求有向图欧拉回路方案数,套用 BEST 定理即可,即以 为根的外向树个数乘上 的度数再乘上每个节点出度 。
特别的,由于这里是记点标号,所以答案除以每个方向内部的方案,即个数的阶乘。
外向树个数直接手模即可,复杂度 。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=2000010,mod=998244353;
int ksm(int a,int b=mod-2)
{
int r=1;
for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) r=1ll*r*a%mod;
return r;
}
int fac[N],inv[N];
void init(int n=N-10)
{
for(int i=fac[0]=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod;
inv[n]=ksm(fac[n]);
for(int i=n-1;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
}
int C(int x,int y){return 1ll*fac[x]*inv[y]%mod*inv[x-y]%mod;}
int a[4],b[4],c[4];
int calc(int x)
{
b[0]=x,c[0]=a[0]-x;
for(int i=1;i<4;i++)
{
b[i]=(b[i-1]-c[i-1]+a[i])/2,c[i]=a[i]-b[i];
if((c[i-1]-b[i-1]+a[i])%2 || b[i]<0 || c[i]<0) return 0;
}
int res=(1ll*b[0]*b[1]*b[2]+1ll*b[0]*b[1]*c[3]+1ll*b[0]*c[3]*c[2]+1ll*c[3]*c[2]*c[1])%mod;
for(int i=0;i<4;i++) res=1ll*res*fac[b[i]+c[(i+3)%4]-1]%mod;
for(int i=0;i<4;i++) res=1ll*res*inv[b[i]]%mod*inv[c[i]]%mod;
return 1ll*res*(b[0]+c[3])%mod;
}
int main()
{
init();for(int i=0;i<4;i++) scanf("%d",&a[i]);
int ans=0;
for(int x=0;x<=a[0];x++) ans=(ans+calc(x))%mod;
printf("%d\n",ans);
return 0;
}
E. Middle Point
题解
设 表示第 个点对应的向量。可以证明,一个点 最终在答案里当且仅当存在一个非负整数数 和一个非负序列 ,满足 且 。
考虑如果没有 非负的限制,这是一个经典问题。具体来说,最后存在一对向量 ,满足一个点 可以被表示当且仅当存在 满足 。求出这个向量可以用类似 gcd 的方法处理。
那么对于 的限制,容易证明所有在凸包内部的可以表示出的点均合法。这样可以看作旋转坐标轴后的凸包内整点数,直接用皮克定理即可。
复杂度 。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100010;
typedef long long ll;
struct node{
int x,y;
node(int x=0,int y=0):x(x),y(y){}
bool operator <(const node a)const{return x==a.x?y<a.y:x<a.x;}
node operator +(const node a)const{return node(x+a.x,y+a.y);}
node operator -(const node a)const{return node(x-a.x,y-a.y);}
ll operator *(const node a)const{return 1ll*x*a.y-1ll*y*a.x;}
}a[N],p[N];
ll gcd(ll x,ll y){return y==0?abs(x):gcd(y,x%y);}
ll cross(node a,node o,node b){return (a-o)*(b-o);}
ll dis(node a){return 1ll*a.x*a.x+1ll*a.y*a.y;}
int main()
{
int n;scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y);
ll g=0;int m=0;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
for(int k=j+1;k<=n;k++) g=gcd(g,cross(a[i],a[j],a[k]));
while(g%2==0) g/=2;
sort(a+1,a+n+1);
for(int i=1;i<=n;i++){while(m>1 && cross(p[m-1],p[m],a[i])<=0) m--;p[++m]=a[i];}
for(int i=n,m0=m;i;i--){while(m>m0 && cross(p[m-1],p[m],a[i])<=0) m--;p[++m]=a[i];}
ll s=0,r=0,ans=0;
for(int i=1;i<m;i++) s+=p[i]*p[i+1];
s=abs(s)/g;
for(int i=1;i<m;i++)
{
node v=p[i+1]-p[i];
ll g0=gcd(v.x,v.y),g1=0;v.x/=g0,v.y/=g0;
for(int j=1;j<=n;j++) g1=gcd(g1,(a[j]-p[i])*v);
g0/=g/gcd(g,g1),r+=g0,ans+=1ll<<__builtin_ctz(g0);
}
printf("%lld\n",ans+(s+2-r)/2);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理