AtCoder Grand Contest 041

链接

A. Table Tennis Training

skipped

B. Voting Judges

skipped

C. Domino Quality

构造题。

考虑我们是可以构造 4×4,5×5,6×6,7×7 的构造,且每行每列都只有 2。

所以直接将矩形对角线分成若干上述矩形即可,其他全部置 0。

注意特判 3。

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100010
using namespace std;
const char s[4][8][8]={
    {
        "abcc",
        "abdd",
        "ccab",
        "ddab"},
    {
        "abbcc",
        "ad..e",
        "ed..e",
        "e.aac",
        "aabbc"},
    {
        "aabbcc",
        ".d.d.d",
        ".d.d.d",
        "d.d.d.",
        "d.d.d.",
        "aabbcc"},
    {
        ".aabbcc",
        "abcc...",
        "ab.a...",
        "bcca...",
        "b...abb",
        "a...a.a",
        "a...bba"}
};
int p=1,n;
char res[1010][1010];
void print(int x)
{
    n-=x+4;
    for(int i=0;i<x+4;i++)
        for(int j=0;j<x+4;j++) res[p+i][p+j]=s[x][i][j];
    p+=x+4;
}
int main()
{
    scanf("%d",&n);
    int m=n;
    if(n<=3)
    {
        if(n<=2) puts("-1");
        else puts("a..\na..\n.bb");
        return 0;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) res[i][j]='.';
    print(n%4);
    while(n) print(0);
    for(int i=1;i<=m;i++) printf("%s\n",res[i]+1);
    return 0;
}

D. Problem Scores

考虑这个 ST 假如存在交集,就意味着交集部分是可以去掉的,显然不影响相对大小,所以不妨钦定 ST 不交。

可以发现最坏情况下一定是 S 取前 n12 大的部分,T 取剩余部分。所以我们只需要保证这时候的结论成立即可。

显然要分奇偶讨论。先考虑偶数的情况。

可以发现原序列我们可以看成,先钦定中间点的位置,然后依次将前缀 -1,后缀 +1。可以发现这是等价的。

分别枚举减哪个前缀和减多少,然后直接 dp 即可。稍微优化一下就是 O(n2logn)

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 5010
using namespace std;
int f[N][N],mod,g[N][N];
int main()
{
    int n;
    scanf("%d%d",&n,&mod);
    f[0][0]=g[0][0]=1;
    for(int i=1;i<=(n+1)/2;i++)
        for(int j=0;j<=n;j++)
            for(int k=j;k<=n;k+=i) f[i][k]=(f[i][k]+f[i-1][j])%mod;
    for(int i=(n-1)/2;i;i--)
        for(int j=0;j<=n && i*j<n;j++)
            for(int k=i*j;k<=n-i;k++) g[j+1][k+i]=(g[j+1][k+i]+g[j][k])%mod;
    for(int i=0;i<=n;i++)
        for(int j=0;j<=n;j++)
        g[i][j]=(1ll*g[i][j]+(i?g[i-1][j]:0)+(j?g[i][j-1]:0)-(i && j?g[i-1][j-1]:0)+mod)%mod;
    int ans=0;
    for(int i=1;i<=n;i++)
        for(int j=0;j<i;j++) ans=(ans+1ll*f[n/2][j]*g[n-i][i-j-1])%mod;
    printf("%d\n",ans);
    return 0;
}

E. Balancing Network

构造好题。

首先考虑第一种情况,即要把所有行都移到同一行。可以发现,如果存在一条边链接 (u,v),那么 u 能到的点我们可以认为 v 也能到,v 同理。

所以考虑算出每个点能到的点。可以证明,如果存在一个点可以被所有点到达,那么一定存在方案。

对于构造考虑从后往前处理。如果一条边链接的两个点中一个点已经构造好了到达目标点的方案,那么另一个点连向它,否则随便连一条。可以证明这样是对的。

然后考虑第二种情况。可以发现只有当 n=2 时无解,否则一定有解。

那么我们只考虑前 3 行,我们最后目的是让至少两个位置不同。所以从后往前,可以发现的是如果有 uv,那么 v 的所有流量都会流到 u 上,反过来,u 的部分流量也可以回到 v 上。那么我们钦定这次操作是由流量少的点向多的点连边。

用 bitset 优化,复杂度 O(n2ω).

#include<iostream>
#include<cstdio>
#include<cstring>
#include<bitset>
#define N 50010
#define M 100010
using namespace std;
int a[N],n,m;
bitset<N>v[N],t;
int x[M],y[M];
bool vis[M],ans[M];
int c[]={0,1,1,1},g[]={0,1,2,3};
int main()
{
    int T;
    scanf("%d%d%d",&n,&m,&T);
    for(int i=1;i<=m;i++) scanf("%d%d",&x[i],&y[i]);
    if(T==1)
    {
        t.set();
        for(int i=1;i<=n;i++) v[i].set(i);
        for(int i=m;i;i--) v[x[i]]=(v[y[i]]|=v[x[i]]);
        for(int i=1;i<=n;i++) t&=v[i];
        for(int i=1;i<=n;i++)
        if(t.test(i))
        {
            vis[i]=true;
            for(int p=m;p;p--)
            if(vis[y[p]]) ans[p]=true,vis[x[p]]=true;
            else vis[y[p]]=vis[x[p]];
            for(int i=1;i<=m;i++) putchar(ans[i]?'v':'^');
            return 0;
        }
        puts("-1");
    }
    else
    {
        if(n<=2){puts("-1");return 0;}
        for(int i=m;i;i--)
        if(y[i]<4)
        {
            ans[i]=c[g[x[i]]]>c[g[y[i]]];
            if(c[g[x[i]]]>c[g[y[i]]]) swap(x[i],y[i]);
            c[g[x[i]]]++,c[g[y[i]]]--;
            g[y[i]]=g[x[i]];
        }
        for(int i=1;i<=m;i++) putchar(ans[i]?'v':'^');
    }
    return 0;
}

F. Histogram Rooks

首先考虑容斥,即钦定有 x 列有格子没有被覆盖。

那么这些格子的对应行和列也一定没有车。

考虑由于有删除格子的部分,每次我们找到当前区间的最低点,先分治左右两边,然后处理当前格子贡献。

可以发现,由于是区间最低点,区间任意高于最低点位置的车都不会影响当前列。所以可以影响的部分变成一个矩形。

考虑 dp。用 fi,j 表示节点 i 的区间中钦定 j 列没有选的方案数。

但是直接这样容斥会有问题,因为我们并不知道这些列是否真的存在格子没有被选中。所以考虑再容斥,即集合 T 中的列虽然没有车,但是被其他列的车覆盖了。

直接转移看起来是 O(n3)。但这其实也是树形 dp 的经典套路,我们每次枚举的长度只有左右区间的长度,那么任意两列的贡献只会被计算一次。复杂度 O(n2)

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 410
#define mod 998244353
using namespace std;
int ksm(int a,int b=mod-2)
{
    int r=1;
    for(;b;b>>=1)
    {
        if(b&1) r=1ll*r*a%mod;
        a=1ll*a*a%mod;
    }
    return r;
}
int f[N][2][N],_2[N],h[N],cnt;
int siz[N];
void solve(int &u,int l,int r,int pre)
{
    if(l>r) return ;
    int x=l;
    for(int i=l+1;i<=r;i++)
    if(h[i]<h[x]) x=i;
    u=++cnt;int ls=0,rs=0;
    solve(ls,l,x-1,h[x]);solve(rs,x+1,r,h[x]);
    auto gl=f[ls],gr=f[rs],g=f[u];
    for(int i=0;i<=siz[ls];i++)
        for(int j=0;j<=siz[rs];j++)
        {
            g[0][i+j]=(g[0][i+j]+1ll*gl[0][i]*gr[0][j])%mod;
            g[1][i+j]=(g[1][i+j]+1ll*gl[1][i]*gr[1][j]-1ll*gl[0][i]*gr[0][j]%mod+mod)%mod;
            g[0][i+j+1]=(g[0][i+j+1]-1ll*gl[0][i]*gr[0][j]%mod+mod)%mod;
            g[1][i+j+1]=(g[1][i+j+1]+1ll*gl[0][i]*gr[0][j])%mod;
        }
    siz[u]=siz[ls]+siz[rs]+1;
    for(int j=0;j<=siz[u];j++)
        g[0][j]=1ll*g[0][j]*ksm(_2[siz[u]-j],h[x]-pre)%mod,
        g[1][j]=(g[0][j]+1ll*g[1][j]*ksm(_2[siz[u]-j]-1,h[x]-pre))%mod;
}
int main()
{
    int n;
    scanf("%d",&n);
    _2[0]=f[0][0][0]=f[0][1][0]=1;
    for(int i=1;i<=n;i++) scanf("%d",&h[i]),_2[i]=_2[i-1]*2%mod;
    int u=0,ans=0;
    solve(u,1,n,0);
    for(int i=0;i<=n;i++) ans=(ans+f[u][1][i])%mod;
    printf("%d\n",ans);
    return 0;
}
posted @   Flying2018  阅读(7)  评论(0编辑  收藏  举报
(评论功能已被禁用)
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示