AtCoder Regular Contest 093~097
AtCoder Regular Contest 093
E. Bichrome Spanning Tree
考虑先建出最小生成树。不妨设为 。
如果 显然无解。
然后求出每一条边强制选中时的最小生成树,不妨设为 。
如果 ,那么最小生成树一定是同色的,对于其他 的边钦定某一条异色, 的边钦定同色,那么其他边任意。
如果 ,如果最小生成树异色,那么其他边任意。否则同上。
F. Dark Horse
考虑钦定你在 位置,最后再乘 即可。这样剩余人可以分成 组,第 组人数 。要求每组的最小值不能是 集合的人。
考虑容斥,钦定 组中的人是 集合的人。考虑 dp,将 集合依次放入。对于一个 ,如果它是某个组的最小值,考虑构造该组的其他部分,显然只有没有放入其他集合的大于 的数可行,直接 dp 集合。
复杂度 。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100010
#define mod 1000000007
using namespace std;
int fac[N],inv[N];
inline void add(int &x,int y){x+y>=mod?x=x+y-mod:x=x+y;}
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 C(int a,int b){return a<b?0:1ll*fac[a]*inv[b]%mod*inv[a-b]%mod;}
void init(int n=N-10)
{
fac[0]=1;
for(int i=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 a[N],f[N];
int main()
{
init();
int n,m,q;
scanf("%d%d",&n,&q);
for(int i=1;i<=q;i++) scanf("%d",&a[i]);
reverse(a+1,a+q+1);
m=1<<n;
f[0]=1;
for(int i=1;i<=q;i++)
for(int s=m-1;~s;s--)
if(f[s])
for(int k=0;k<n;k++)
if(!(s>>k&1)) add(f[s^(1<<k)],1ll*f[s]*C(m-s-a[i],(1<<k)-1)%mod*fac[1<<k]%mod);
int ans=0;
for(int i=0;i<m;i++) add(ans,(__builtin_popcount(i)&1?mod-1ll:1ll)*f[i]%mod*fac[m-i-1]%mod);
printf("%lld",(1ll<<n)*ans%mod);
return 0;
}
AtCoder Regular Contest 094
AtCoder Regular Contest 095
F. Permutation Tree
假设有一个指针维护当前最大值,那么从右往左推的过程中每次都会使若干个点连向当前最大值,最后连出的一定是一个链套菊花(毛毛虫)。
考虑钦定,一个顺序,直接按 构造即可。
复杂度 。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define N 100010
using namespace std;
vector<int>g[N];
int deg[N];
vector<int>f;
vector<int> solve(int n)
{
vector<int>res(n);
int tt=0,p=0;
for(int u:f)
{
res[p+deg[u]]=++tt;
for(int j=p;j<p+deg[u];j++) res[j]=++tt;
p+=deg[u]+1;
}
if(tt<n){puts("-1");exit(0);}
return 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);
for(int u=1;u<=n;u++)
if(g[u].size()>1)
{
int c=0;
for(int v:g[u]) if(g[v].size()>1) c++;
if(c==1)
{
for(int v:g[u]) if(g[v].size()==1){f.push_back(v);break;}
f.push_back(u);
for(int v:g[u]) if(g[v].size()>1) u=v;
while(true)
{
int p=f.back();f.push_back(u);
for(int v:g[u]) if(g[v].size()>1 && v!=p){u=v;break;}
if(u==f.back())
{
for(int v:g[u]) if(g[v].size()==1 && v!=p){f.push_back(v);break;}
break;
}
}
break;
}
}
if(!f.size())
{
for(int u=1;u<=n;u++)
if(g[u].size()>1)
{
for(int v:g[u]) if(g[v].size()==1){f.push_back(v);break;}
f.push_back(u);
for(int v:g[u]) if(g[v].size()==1 && v!=*f.begin()){f.push_back(v);break;}
break;
}
if(!f.size()){puts("1 2");return 0;}
}
for(int i=0;i<f.size();i++) if(i==0 || i==f.size()-1) deg[f[i]]=g[f[i]].size()-1;
else deg[f[i]]=g[f[i]].size()-2;
// for(int v:f) printf("%d ",v);puts("");
// for(int i=1;i<=n;i++) printf("%d ",deg[i]);puts("");
auto ans=solve(n);
reverse(f.begin(),f.end());
ans=min(ans,solve(n));
for(int v:ans) printf("%d ",v);
return 0;
}
AtCoder Regular Contest 096
E. Everything on It
考虑容斥,假设钦定 个数不到 。换句话说要么 要么 。
考虑钦定一个垃圾桶集合,这样所有 的数认为扔到垃圾桶集合即可。
为了防止记重,再额外规定一个特殊的数,这个数所在集合就是垃圾桶集合。
这部分答案就是第二类斯特林数 。
考虑剩下的数,要么任意组成集合方案数为 ,或者往前面集合扔方案数为 。直接 dp 即可。
复杂度 。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 3012
using namespace std;
int mod,C[N][N],S[N][N];
int f[N],_2[N*N];
void init(int n=N-10)
{
C[0][0]=S[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=C[i][0]=1;j<=n;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod,
S[i][j]=(S[i-1][j-1]+1ll*S[i-1][j]*j)%mod;
for(int i=_2[0]=1;i<=n*n;i++) _2[i]=2ll*_2[i-1]%mod;
}
int ksm(int a,int b=mod-2,int m=mod)
{
int r=1;
for(;b;b>>=1,a=1ll*a*a%m) if(b&1) r=1ll*r*a%m;
return r;
}
int main()
{
int n;
scanf("%d%d",&n,&mod);
init();
for(int i=0;i<=n;i++)
for(int j=0;j<=i;j++) f[i]=(f[i]+1ll*S[i+1][j+1]*_2[(n-i)*j])%mod;
int ans=0;
for(int i=0;i<=n;i++) ans=(ans+(i&1?mod-1ll:1ll)*C[n][i]%mod*ksm(2,ksm(2,n-i,mod-1))%mod*f[i])%mod;
printf("%d\n",ans);
return 0;
}
F. Sweet Alchemy
考虑这显然是一个树形 dp,子树和一下等价于除了 号点其他点只能选 个。
考虑直接 dp 可以做到 ,显然过不去。
考虑一种错误的贪心:按 排序,从大到小加。考虑要使这个结论正确,我们需要对于偏序 ,有 可以选 个,因为这样换成若干 选 个更优。
所以只需要对 求背包即可。
考虑用二进制分组优化,复杂度 。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define ll long long
using namespace std;
const int N=60;
ll c[N];
int siz[N],n,x,d;
vector<int>g[N],h[N];
void dfs(int u)
{
siz[u]=1;h[u].push_back(u);
for(int v:g[u])
{
dfs(v);
for(int i:h[v]) h[u].push_back(i);
siz[u]+=siz[v];
c[u]+=c[v];
}
}
const int M=N*N*N;
ll f[M];
int solve(int m)
{
memset(f,0x3f,sizeof(f));
int mx=0,m0=m;
vector<int>fact;
for(int i=0;(1<<i)<=m;i++) fact.push_back(1<<i),m-=(1<<i);
if(m) fact.push_back(m);
f[0]=0;
for(int v=1;v<=n;v++)
{
mx+=siz[v]*m0;
for(int w:fact)
for(int i=mx;i>=siz[v]*w;i--)
f[i]=min(f[i],f[i-siz[v]*w]+c[v]*w);
}
return mx;
}
int id[N];
int main()
{
scanf("%d%d%d",&n,&x,&d);
scanf("%d",&c[1]);
for(int i=2,p;i<=n;i++) scanf("%d%d",&c[i],&p),g[p].push_back(i);
dfs(1);
int m=min(n,d),mx=solve(m);
for(int i=1;i<=n;i++) id[i]=i;
sort(id+1,id+n+1,[&](int x,int y){return c[x]*siz[y]<c[y]*siz[x];});
int ans=0;
for(int i=0;i<=mx;i++)
if(f[i]<=x)
{
int res=i,v=f[i];
for(int j=1;j<=n;j++)
{
int u=id[j];
if(u==1)
{
ll w=(x-v)/c[u];
res+=w*siz[u];
break;
}
int w=min((ll)d-m,(x-v)/c[u]);
res+=w*siz[u],v+=w*c[u];
}
ans=max(ans,res);
}
printf("%d\n",ans);
return 0;
}
AtCoder Regular Contest 097
F. Monochrome Cat
如果最后要求在出发点结束,那么每条边一定会被经过两次,故答案是分子。所以本质上就是选一条链要求减少的步数尽可能少。
找到一个不可能被去掉的点(白点),直接 dfs 一下即可。
复杂度 。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define N 100010
using namespace std;
int dep[N];bool col[N],sc[N];
char s[N];
vector<int>g[N];
int ans=0,res=0;
void dfs(int u,int p)
{
sc[u]=col[u];int d=col[u]^1;
if(p) d^=1;
for(int v:g[u]) if(v!=p)
{
dfs(v,u);
if(sc[v]) continue;
sc[u]=false;d^=1;
}
if(sc[u]) return;
dep[u]=d*2;ans+=d;
for(int v:g[u]) if(v!=p && !sc[v])
{
res=max(res,dep[u]+dep[v]);
dep[u]=max(dep[u],dep[v]+d*2);
ans+=2;
}
}
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);
scanf("%s",s+1);
for(int i=1;i<=n;i++) col[i]=s[i]=='B';
int rt=0;for(int i=1;i<=n;i++) if(!col[i]){rt=i;break;}
if(!rt){puts("0");return 0;}
dfs(rt,0);
printf("%d\n",ans-res);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 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编程运行原理