AtCoder Grand Contest 024

链接

D. Isomorphism Freak

大水题。可以证明任意染色方案都可以转化为以某个点(或边的中点)为根时任意点所有儿子同构。

由于 n 很小,直接枚举以哪个点(边)为根,直接统计某一深度的儿子最大值再累乘即可。

可以发现度数为 2 的点没有贡献,度数为 3 的点深度最多是 50,总答案在 long long 范围内。

复杂度 O(n2)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define N 210
#define ll long long
using namespace std;
vector<int>g[N];
int son[N];
void dfs(int u,int p,int d)
{
    int t=0;
    for(int v:g[u]) if(v!=p) dfs(v,u,d+1),++t;
    son[d]=max(son[d],t);
}
int md;ll ans;
void solve(int u,int v)
{
    if(v==0)
    {
        int d=0;
        memset(son,0,sizeof(son));
        dfs(u,0,1);
        long long res=1;
        for(d=1;son[d];d++) res*=son[d];
        if(d>md) return;
        if(d==md) ans=min(ans,res);
        else md=d,ans=res;
    }
    else
    {
        int d=0;
        memset(son,0,sizeof(son));
        dfs(u,v,1);dfs(v,u,1);son[0]=2;
        long long res=1;
        for(d=0;son[d];d++) res*=son[d];
        if(d>md) return;
        if(d==md) ans=min(ans,res);
        else md=d,ans=res;
    }
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),g[u].push_back(v),g[v].push_back(u);
    md=n;
    for(int i=1;i<=n;i++) solve(i,0);
    for(int u=1;u<=n;u++) for(int v:g[u]) if(u<v) solve(u,v);
    printf("%d %lld\n",md,ans);
    return 0;
}

E. Sequence Growing Hard

考虑什么情况下会有计重问题:比如 122312223,往中间三个位置插入 2 都是等价的。

那么我们不妨假设永远在相同字典序最后加字符。可以发现这样规定后,字典序递增也解决了:只要插入的字符字典序大于插入前的字符即可。

这样可以设计一个 dp:令 fi,j,k 表示当前长度为 i,加入的字符是 j,当前插入字符前面有 k 个字符。

如果当前位置不放置字符,那么 fi,j,kfi,j,k1

如果不放置当前数字,那么 fi,j,kfi,j+1,k,但为了防止记重,这里要求已经不能放置该字符,即 k=0

考虑如果当前位置插入字符,看似只有一种决策,但是注意这种情况:1333111333121。此时插入的字符并不是 j。看起来 dp 状态有问题?但是注意我们要求插入的顺序必须从小到大,即上述字符我们应当先插入 2 再插入 3。换句话说,除了插入之外还有一个删除的顺序。那么对于前面还有 k 个字符的位置来说,就有 k+1 种删除的顺序,所以应该有 fi,j,k×(k+1)fi+1,j,k

总复杂度 O(n3)

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

F. Simple Subsequence Problem

题意

给定若干长度不超过 n 的字符串,问满足是其中 k 个字符串子序列的字符串最长是多少,在此前提下要求字典序最小。n20

题解

经典 DFA 题。

考虑有种东西叫做序列自动机,就是每次贪心往后找某个字符第一次出现的位置。

对于这道题,注意到事实上可行串的个数并不多,所以考虑对于每一个串都求出它是多少个串的子串。

具体来说,定义状态 A|B 表示剩余能匹配的字符串是 B,已经匹配的字符串是 A。转移可以直接停止(A|ϵ),匹配 1A1|B1),匹配 0,(A0|B0)。

注意虽然 A,B 都是 2n,但是 AB 总长度是 n,所以总状态数 n2n。这里有一个小 trick,即记录状态 1AB|B|,这样找到第一个 1,就能唯一确定一组 {A,B}

最后一遍扫描即可。总复杂度 O(2nn)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<bitset>
#define N 21
using namespace std;
int f[1<<N][N],_2[N+2];
short c0[1<<N],c1[1<<N];//c0,c1:1x,标志 1 后第一个 0 的位置和第一个 1 的位置
int pre0(int x,int lx){return 1<<lx|x;}
int calc(int x,int y,int ly){return (x<<ly)|y;}
char s[1<<N];
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    int m=1<<(n+1);
    for(int i=1;i<=n+1;i++) _2[i]=_2[i-1]<<1|1;
    memset(c0,-1,sizeof(c0));memset(c1,-1,sizeof(c1));
    for(int i=2;i<m;i++)
    {
        if(c0[i>>1]!=-1) c0[i]=c0[i>>1]+1;
        if(c1[i>>1]!=-1) c1[i]=c1[i>>1]+1;
        if((i&1) && c1[i]==-1) c1[i]=0;
        if(!(i&1) && c0[i]==-1) c0[i]=0;
    }
    for(int i=0;i<=n;i++)
    {
        scanf("%s",s);
        for(int j=0;j<1<<i;j++) if(s[j]=='1') f[calc(1,j,i)][i]++;
    }
    for(int i=n;i>0;i--)
        for(int s=1;s<m;s++)
        if(f[s][i])
        {
            int t=s>>i,u=pre0(s&_2[i],i);
            // cout<<bitset<5>(s)<<" "<<i<<" "<<bitset<5>(u)<<" : ";
            if(c0[u]!=-1)
            {
                int v=t<<(c0[u]+1)|(s&_2[c0[u]]);
                f[v][c0[u]]+=f[s][i];
                // cout<<bitset<5>(v)<<","<<c0[u]<<" ";
            }
            if(c1[u]!=-1)
            {
                int v=t<<(c1[u]+1)|(1<<c1[u])|(s&_2[c1[u]]);
                f[v][c1[u]]+=f[s][i];
                // cout<<bitset<5>(v)<<","<<c1[u]<<" ";
            }
            f[t][0]+=f[s][i];
            // cout<<" "<<f[s][i]<<endl;
        }
    int mn=m,len=0;
    for(int s=1;s<m;s++)
    if(f[s][0]>=k)
    {
        int t=-1;
        for(int i=n;i>=0;i--) if(s>>i&1){t=i;break;}
        if(t>len) len=t,mn=s;
        else if(t==len) mn=min(mn,s);
    }
    for(int i=len-1;i>=0;i--) printf("%d",mn>>i&1);
    return 0;
}
posted @   Flying2018  阅读(4)  评论(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编程运行原理
点击右上角即可分享
微信分享提示