AtCoder Grand Contest 024
链接
D. Isomorphism Freak
大水题。可以证明任意染色方案都可以转化为以某个点(或边的中点)为根时任意点所有儿子同构。
由于 \(n\) 很小,直接枚举以哪个点(边)为根,直接统计某一深度的儿子最大值再累乘即可。
可以发现度数为 \(2\) 的点没有贡献,度数为 \(3\) 的点深度最多是 50,总答案在 long long 范围内。
复杂度 \(O(n^2)\)。
#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
考虑什么情况下会有计重问题:比如 \(1223\rightarrow 12223\),往中间三个位置插入 \(2\) 都是等价的。
那么我们不妨假设永远在相同字典序最后加字符。可以发现这样规定后,字典序递增也解决了:只要插入的字符字典序大于插入前的字符即可。
这样可以设计一个 dp:令 \(f_{i,j,k}\) 表示当前长度为 \(i\),加入的字符是 \(j\),当前插入字符前面有 \(k\) 个字符。
如果当前位置不放置字符,那么 \(f_{i,j,k}\rightarrow f_{i,j,k-1}\)。
如果不放置当前数字,那么 \(f_{i,j,k}\rightarrow f_{i,j+1,k}\),但为了防止记重,这里要求已经不能放置该字符,即 \(k=0\)。
考虑如果当前位置插入字符,看似只有一种决策,但是注意这种情况:\(133311\rightarrow 1333121\)。此时插入的字符并不是 \(j\)。看起来 dp 状态有问题?但是注意我们要求插入的顺序必须从小到大,即上述字符我们应当先插入 \(2\) 再插入 \(3\)。换句话说,除了插入之外还有一个删除的顺序。那么对于前面还有 \(k\) 个字符的位置来说,就有 \(k+1\) 种删除的顺序,所以应该有 \(f_{i,j,k}\rightarrow^{\times (k+1)} f_{i+1,j,k}\)
总复杂度 \(O(n^3)\)。
#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\) 个字符串子序列的字符串最长是多少,在此前提下要求字典序最小。\(n\leq 20\)。
题解
经典 DFA 题。
考虑有种东西叫做序列自动机,就是每次贪心往后找某个字符第一次出现的位置。
对于这道题,注意到事实上可行串的个数并不多,所以考虑对于每一个串都求出它是多少个串的子串。
具体来说,定义状态 \(A|B\) 表示剩余能匹配的字符串是 \(B\),已经匹配的字符串是 \(A\)。转移可以直接停止(\(A|\epsilon\)),匹配 \(1\)(\(A1|B_1\)),匹配 \(0\),(\(A0|B_0\))。
注意虽然 \(A,B\) 都是 \(2^n\),但是 \(A\) 和 \(B\) 总长度是 \(n\),所以总状态数 \(n2^n\)。这里有一个小 trick,即记录状态 \(1AB\) 和 \(|B|\),这样找到第一个 \(1\),就能唯一确定一组 \(\{A,B\}\)。
最后一遍扫描即可。总复杂度 \(O(2^nn)\)。
#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;
}

浙公网安备 33010602011771号