AtCoder Grand Contest 051

链接

B. Bowling

题解

明哥题。题解给了一个 rand 的做法。明哥直接硬构造了一个分形。

3
33
000
1002
11022

其中 1,2,3 分别递归构造这个结构。

对于 d 层的构造,可以做到 (32)d 的比例,消耗 3d 的节点。

d=10,大概可以做到 50 倍左右。

代码
#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

题解

首先这类题有一个经典套路,考虑贪心以黑点为右下角往左往上推,可以证明无论怎么推最终得到的矩形是相同的。此时只有第一行和前两列可能存在黑点。

容易发现,排除平凡的全白点情况,对于某一行的黑点,10 表示需要匹配列标号 mod3=1 的列黑点,01 表示匹配 mod3=211 匹配 mod3=0

这样可以把整个矩形看做一个二分图,左右各有 3 类点 0,1,2。相同类点可以直接匹配,同时左侧 0 可以花 2 代价匹配右侧 1,2,同理 1 匹配 0,22 匹配 0,1。最后每个同侧的同类点可以内部花 2 代价两两匹配。

容易发现,如果同时存在 0 匹配了 1,21 匹配了 0,2,那么此时直接匹配 (0,0),(1,1) 更优。所以最多只有一个数采取匹配两个右侧不同类点。

考虑枚举这类匹配的次数,剩下的一定先贪心异侧匹配,然后再同侧匹配。特别的,如果某类点两侧奇偶性不同,匹配直接不合法。

复杂度 O(nlogn)。瓶颈在离散化。

代码
#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

题解

考虑枚举 aST 的经过次数,这样可以推出每条边每个方向的经过次数。这样就变成了求有向图欧拉回路方案数,套用 BEST 定理即可,即以 1 为根的外向树个数乘上 1 的度数再乘上每个节点出度 1

特别的,由于这里是记点标号,所以答案除以每个方向内部的方案,即个数的阶乘。

外向树个数直接手模即可,复杂度 O(a)

代码
#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

题解

ai 表示第 i 个点对应的向量。可以证明,一个点 v 最终在答案里当且仅当存在一个非负整数数 w 和一个非负序列 pi,满足 piai=2waipi=2w

考虑如果没有 pi 非负的限制,这是一个经典问题。具体来说,最后存在一对向量 {a,b},满足一个点 v 可以被表示当且仅当存在 x,y 满足 v=xa+yb。求出这个向量可以用类似 gcd 的方法处理。

那么对于 pi 的限制,容易证明所有在凸包内部的可以表示出的点均合法。这样可以看作旋转坐标轴后的凸包内整点数,直接用皮克定理即可。

复杂度 O(n2)

代码
#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;
}
posted @   Flying2018  阅读(9)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示