AtCoder Grand Contest 052
链接
B. Tree Edges XOR
题解
考虑这类题目的常见套路是寻找一个不动量,即找到一个 满足 操作后变成 时 不变,然后尝试证明两个集合 可以互相到达当且仅当 。
在这里不妨对每个点构造点权 ,使得任意一条边 满足 。但是注意到只有这样 不是唯一的。由于题面规定了 是奇数,所以我们令 ,这样就存在唯一的 了。
容易证明,任意一次操作后的结果只会交换两个点的点权。所以只需要看点权集合是否一致即可。复杂度 。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100010;
int nxt[N<<1],to[N<<1],w1[N<<1],w2[N<<1],a[N],b[N],head[N],cnt;
void add(int u,int v,int W1,int W2){nxt[++cnt]=head[u];to[cnt]=v;w1[cnt]=W1,w2[cnt]=W2;head[u]=cnt;}
void dfs(int u,int p){for(int i=head[u];i;i=nxt[i]) if(to[i]!=p){int v=to[i];a[v]=a[u]^w1[i],b[v]=b[u]^w2[i],dfs(v,u);}}
int main()
{
int n,x1=0,x2=0;scanf("%d",&n);
for(int i=1;i<n;i++)
{
int u,v,w1,w2;scanf("%d%d%d%d",&u,&v,&w1,&w2);
add(u,v,w1,w2),add(v,u,w1,w2);
}
dfs(1,0);
for(int i=1;i<=n;i++) x1^=a[i],x2^=b[i];
for(int i=1;i<=n;i++) a[i]^=x1,b[i]^=x2;
sort(a+1,a+n+1),sort(b+1,b+n+1);
for(int i=1;i<=n;i++) if(a[i]!=b[i]){puts("NO");return 0;}
puts("YES");
return 0;
}
C. Nondivisible Prefix Sums
题解
明哥题。
考虑先找到判断一个序列是否是“好的”的充要条件。首先显然整个序列的和模 不能是 。可以注意到,假如接下来我们想要填 ,但是 不合法,那么我们先填一个其他数字,那么 一定可以在下一次被填上。所以可以证明,如果一个长度为 的序列中不存在绝对众数,那么它一定合法。
假如一个序列中存在绝对众数,不妨设其为 。由于 是质数,我们让所有数字 ,序列的合法性不变。所以不妨设绝对众数为 。
考虑此时的策略。容易发现,只要某一刻 不再是绝对众数,这个序列就合法了。所以我们一定会尽可能多地填 。注意不能填 时必然是 ,那么此时填一个数字 在相当于给 续了 的长度。
可以发现,这个序列合法当且仅当对于所有 , 不小于 的个数。
那么我们直接枚举非 的数字之和,要求这个数字不能是 倍数,并且显然这个数 。然后可以根据这个推出序列所有元素之和。这个就是拆分数的前缀和,可以直接预处理得到。
而所有是 倍数的方案数可以直接通过容斥 或者背包得到。复杂度 。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=5010,mod=998244353;
int _p[N],f[N],g[N];
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;
}
void add(int &x,int y){x=(x+y>=mod?x+y-mod:x+y);}
void dec(int &x,int y){x=(x-y<0?x-y+mod:x-y);}
int main()
{
int n,p; scanf("%d%d",&n,&p);
f[0]=1;
for(int i=1;i<=n;i++)
{
g[0]=f[0];for(int j=1;j<=n;j++) g[j]=(g[j-1]+f[j])%mod;
for(int j=2;j<=n;j++) add(f[j],(g[j-2]-(j>=p?g[j-p]:0)+mod)%mod);
}
int res=1ll*(ksm(p-1,n)+(n&1?mod-(p-1):(p-1)))*ksm(p)%mod;
res=(ksm(p-1,n)-res+mod)%mod;
for(int i=0;i<=n-p;i++) if((n-i)%p) dec(res,1ll*(p-1)*f[i]%mod);
printf("%d\n",res);
return 0;
}
D. Equal LIS
题解
结论题。设序列 LIS 为 , 表示以 结尾的 LIS 长度,考虑强行构造:
- 如果 是偶数,那么将 的序列划给集合 ,其余划给集合 。首先如果集合中有 的 LIS,从 开始到 结束,那么 ,显然不符合条件。同样集合中也必然存在长度为 的 LIS。所以这样构造就是合法的。
- 如果 是奇数,结论是存在 在长度至少为 的 LIS 中并且不在某一个上升序列中。求一个数是否一定在 LIS 中有点麻烦,事实上还可以证明的是如果存在,那么任意取一个 LIS 都会存在这样的点。
考虑直接构造。如果存在这样的数,考虑取出包含 的任意长度为 的 LIS。设序列为 ,然后令集合 包含所有 等于某个 ,特别的,如果 则划给 集合。容易发现, 集合中上升子序列必然为 。而 中删去了所有除了 外在 集合的所有层,所以上升子序列是 ,构造合法。
复杂度 。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=200010;int n,f[N],g[N],a[N],t[N];
void clear(){for(int i=1;i<=n;i++) t[i]=0;}
void add(int x,int v){for(;x<=n;x+=x&-x) t[x]=max(t[x],v);}
int qry(int x){int v=0;for(;x;x-=x&-x) v=max(v,t[x]);return v;}
int main()
{
int T;scanf("%d",&T);
while(T --> 0)
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
clear();for(int i=1;i<=n;i++) add(a[i],f[i]=qry(a[i])+1);
int res=qry(n);
if(res%2==0){puts("YES");continue;}
clear();for(int i=n;i;i--) add(n-a[i]+1,g[i]=qry(n-a[i]+1)+1);
bool can=false;
for(int i=n,j=res;i;j-=f[i]==j,i--) if(f[i]!=j && f[i]+g[i]>res/2+1){can=true;break;}
puts(can?"YES":"NO");
for(int i=1;i<=n;i++) f[i]=g[i]=0;
}
return 0;
}
E. 3 Letters
题解
考虑构造这样一个序列:令 分别为 ,构造 使得 且 。
容易发现,对于一个给定的 ,固定 那么整个 都是固定的。每次操作等价于找到一个极小或者极大的位置,然后将其 。
结论:答案为 ,当然 和 奇偶性必须相同。这显然是一个下界。对于上界,考虑给出这样一个构造:如果 ,直接跳过该位置。如果 且 ,直接将 减 。否则不妨假设 ,那么找到第一个满足 的位置,注意到由于这一段斜率为 ,所以一定有 ,那么将这一段整体向下移 ,仍然符合答案。
复杂度 。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理