Educational Codeforces Round 132 E,F
E
容易发现,我们在一个点进行修改,一定可以将经过这个点的非法路径数变成。具体的方案就是将异或上,其中,并且要两两不同。(比如就是一组可行的)
此时我们有一个贪心策略:为了让更多非法路径变成合法的,那么我们修改的点深度要尽可能小。
那么我们从叶子向根考虑:如果当前点,存在一个非法路径,满足,那么我们一定修改,否则再往上就没有这条路径的点了,也无法让这条路径变成合法的了。
如何判断是否存在一个非法路径,满足呢?我们发现:设为从根到点的路径的异或值,那么路径的权值异或和就等于。那么用存储当前子树的的集合,在遇到一个儿子时,对于,我们查询是否在中出现过,如果有,那么意味着有一条异或和等于的路径,其lca等于pos。最后再将并入中即可。
注意修改之后,我们不仅让的非法路径成为合法路径,同时还让所有经过往根走的非法路径也变成合法的了。故如果我们要修改当前点,那么需要清空。
具体代码如下:
void cnt(int pos,int fa=0) {
int is=0;
val[pos].insert(f[pos]);
for(auto nxt : G[pos]) {
if(nxt==fa) continue;
cnt(nxt,pos);
for(auto item : val[nxt]) {
if(val[pos].find(item^a[pos])!=val[pos].end()) {
is=1;
}
}
val[pos].insert(val[nxt].begin(),val[nxt].end());
}
if(is) {
ans++;
val[pos].clear();
}
}
但是这样时间复杂度有可能达到,因为将并入中这个操作有可能是的。如果我们采用启发式合并(将小的Set合并到大的Set中),那么时间复杂度将优化到。
整体代码如下:
#include<bits/stdc++.h>
#define debug(...) std::cerr<<#__VA_ARGS__<<" : "<<__VA_ARGS__<<std::endl
const int maxn=200005;
int n,ans;
int a[maxn],f[maxn];
std::set<int> val[maxn];
std::vector<int> G[maxn];
void dfs(int pos,int fa=0) {
f[pos]=f[fa]^a[pos];
for(auto nxt : G[pos]) {
if(nxt==fa) continue;
dfs(nxt,pos);
}
}
void cnt(int pos,int fa=0) {
int is=0;
val[pos].insert(f[pos]);
for(auto nxt : G[pos]) {
if(nxt==fa) continue;
cnt(nxt,pos);
if(val[pos].size()<val[nxt].size()) {
std::swap(val[pos],val[nxt]);
}
for(auto item : val[nxt]) {
if(val[pos].find(item^a[pos])!=val[pos].end()) {
is=1;
}
}
val[pos].insert(val[nxt].begin(),val[nxt].end());
}
if(is) {
ans++;
val[pos].clear();
}
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
}
for(int i=1;i<=n-1;i++) {
int x,y; scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
dfs(1);
cnt(1);
printf("%d\n",ans);
return 0;
}
F
我们将这个问题放到Trie上考虑。容易发现我们其实是要选择若干个叶子,并且其实就是对一个子树限制其内部选择的叶子个数不能超过。
那么此时我们可以在Trie上DP。设表示考虑为根的子树,我们最多能选择个叶子,则我们大致可以列出一个转移式:
其中是当前点的取值数。
现在我们就来算一下的可能取值:
首先,那么显然不可能,故。
若,那么,。
若,由于当前方案应该是选择最多点的,若也大于,那么意味着我们可以取更多的叶子。故,。
那么我们可以写出具体的转移方程:
边界:在叶子上,如果我们选择个点,并且这还是选择点最多的方案,显然。所以:
边界:在根节点上,我们没有对应的,所以的方案是不存在的。
答案:答案就是。(为了防止变量重复,这里的就是输入时的)要特别注意,有时候也是有方案的,比如,此时要特别注意处理到根节点是否考虑了这种情况。
这样做是的。虽然转移的式子一眼卷积,但也仅仅能优化到。
注意到一个奇特的性质——对于相同深度的点,所对应的子树其实长得是一样的。(因为这个Trie是棵满二叉树)所以对于深度相同的点,其DP值也是相同的。
所以修改DP数组的定义——设表示深度为的点,我们取个叶子的方案数。
转移方程也是类似的:
此时再用卷积+后缀和,(FFT或NTT)时间复杂度降为,就可以通过此题了。
#include<bits/stdc++.h>
#define debug(...) std::cerr<<#__VA_ARGS__<<" : "<<__VA_ARGS__<<std::endl
const int mod=998244353;
int qpow(int x,int y) {
int ret=1;
for(;y;y>>=1,x=1ll*x*x%mod) {
if(y&1) {
ret=1ll*ret*x%mod;
}
}
return ret;
}
namespace mul {
const int N=2097152;
const int g=3;
int omega[N];
void init() {
int Wn=qpow(g,(mod-1)/N);
omega[0]=1;
for(int i=1;i<N;i++) {
omega[i]=1ll*omega[i-1]*Wn%mod;
}
}
void ntt(int *a,int n,int op) {
for(int i=0,j=0;i<n;i++) {
if(i<j) std::swap(a[i],a[j]);
int k=n; while(j<k) k>>=1,j^=k;
}
for(int k=1,step=N>>1;k<n;k<<=1,step>>=1) {
for(int i=0;i<n;i+=k<<1) {
for(int j=i,cur=0;j<i+k;j++,cur+=step) {
int tmp=1ll*a[j+k]*omega[cur]%mod;
a[j+k]=a[j]-tmp+mod;
a[j]+=tmp;
if(a[j+k]>=mod) a[j+k]-=mod;
if(a[j]>=mod) a[j]-=mod;
}
}
}
if(op==-1) {
std::reverse(a+1,a+n);
int iv2=(mod+1)/2,ivn=1;
for(int i=1;i<n;i<<=1) {
ivn=1ll*ivn*iv2%mod;
}
for(int i=0;i<n;i++) {
a[i]=1ll*a[i]*ivn%mod;
}
}
}
} using namespace mul;
int n,k,t;
int A[600005],B[600005],S[600005];
int f[20][200005];
int main() {
init();
scanf("%d%d%d",&n,&k,&t);
int len=1; while(len<=k+k) len<<=1;
for(int i=0;i<=k;i++) f[n][i]=1;
for(int i=n-1;i>=0;i--) {
memset(A,0,sizeof A);
memset(B,0,sizeof B);
memset(S,0,sizeof S);
for(int j=0;j<=k;j++) B[j]=f[i+1][j];
ntt(B,len,1);
for(int j=0;j<len;j++) A[j]=1ll*B[j]*B[j]%mod;
ntt(A,len,-1);
for(int j=len-1;j>=0;j--) {
S[j]=A[j]+S[j+1]; if(S[j]>=mod) S[j]-=mod;
}
if(!i) {
std::cout<<A[t]<<std::endl; exit(0);
} else {
for(int j=0;j<=k;j++) {
f[i][j]=1ll*(k-j+1)*A[j]%mod+S[j+1];
if(f[i][j]>=mod) f[i][j]-=mod;
}
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话