AtCoder Grand Contest 041
链接
A. Table Tennis Training
B. Voting Judges
C. Domino Quality
构造题。
考虑我们是可以构造 ,,, 的构造,且每行每列都只有 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
考虑这个 和 假如存在交集,就意味着交集部分是可以去掉的,显然不影响相对大小,所以不妨钦定 与 不交。
可以发现最坏情况下一定是 取前 大的部分, 取剩余部分。所以我们只需要保证这时候的结论成立即可。
显然要分奇偶讨论。先考虑偶数的情况。
可以发现原序列我们可以看成,先钦定中间点的位置,然后依次将前缀 -1,后缀 +1。可以发现这是等价的。
分别枚举减哪个前缀和减多少,然后直接 dp 即可。稍微优化一下就是 。
#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
构造好题。
首先考虑第一种情况,即要把所有行都移到同一行。可以发现,如果存在一条边链接 ,那么 能到的点我们可以认为 也能到, 同理。
所以考虑算出每个点能到的点。可以证明,如果存在一个点可以被所有点到达,那么一定存在方案。
对于构造考虑从后往前处理。如果一条边链接的两个点中一个点已经构造好了到达目标点的方案,那么另一个点连向它,否则随便连一条。可以证明这样是对的。
然后考虑第二种情况。可以发现只有当 时无解,否则一定有解。
那么我们只考虑前 3 行,我们最后目的是让至少两个位置不同。所以从后往前,可以发现的是如果有 ,那么 的所有流量都会流到 上,反过来, 的部分流量也可以回到 上。那么我们钦定这次操作是由流量少的点向多的点连边。
用 bitset 优化,复杂度 .
#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
首先考虑容斥,即钦定有 列有格子没有被覆盖。
那么这些格子的对应行和列也一定没有车。
考虑由于有删除格子的部分,每次我们找到当前区间的最低点,先分治左右两边,然后处理当前格子贡献。
可以发现,由于是区间最低点,区间任意高于最低点位置的车都不会影响当前列。所以可以影响的部分变成一个矩形。
考虑 dp。用 表示节点 的区间中钦定 列没有选的方案数。
但是直接这样容斥会有问题,因为我们并不知道这些列是否真的存在格子没有被选中。所以考虑再容斥,即集合 中的列虽然没有车,但是被其他列的车覆盖了。
直接转移看起来是 。但这其实也是树形 dp 的经典套路,我们每次枚举的长度只有左右区间的长度,那么任意两列的贡献只会被计算一次。复杂度 。
#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;
}
【推荐】国内首个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编程运行原理