GDKOI2023 游记
这两天的模拟赛是 GDKOI。
D1T1 和 D2T1 都是相对较水,而且数据水,放过了许多错解。
D1T2 和 D2T2 是困难题,D1T3 和 D2T3 是可做题。听说 D2T3 用到的是一个冬令营介绍的 separator 科技,D2T2 用到了一个非常冷门的 BM 线性递推。目前我只懂了 D1T3 和 D2T3,另外两题只会暴力。考场上唯二(还是唯三,忘了)做出 D1T2 的其中之一是一位初一的 Au 巨佬,他讲的方法虽然比较通俗但我听完就忘了。
D1T1 矩阵
判断 的矩阵 是否 。
正解是随一个 向量 去分别乘以等式左右,判断等号是否成立。我的做法是:由于 的暴力矩乘可以这样写:
复制for(i,1,n)for(j,1,n)for(k,1,n)c[i][k]-=a[i][j]*b[j][k];
然后判断 c
是否是全零矩阵。所以可以直接哈希掉 和 的每一行,这样就不用枚举 了。
D1T2
求满足 的 排列 的方案数。
略。
D1T3
给定 、序列 、二元组序列 ,求有多少个序列 ,满足:
的版本就是 (Topcoder SRM) DefectiveAddition 那题,简单来说就是:枚举第一个(最高的)存在一个 没顶上界的位 ,那么后面的位就至少存在一个完全自由的数,它可以让其他数选完自己再选上 从而始终能够满足条件,因此后面的选法就是 。每层用一个 的 dp 解决,总复杂度 。
考虑 的情况。发现“图”的形式并不是没有意义的,因为“”具有传递性,选若干条边可以形成若干连通块。不难想到一个暴力的容斥: 枚举边集的钦定情况,把每个大小为奇数的连通块看成一个数(偶数直接忽略),其上界为连通块内所有点的上界的最小值,然后做 的版本,容斥系数为 。
题目自然想让我们把边集上的容斥系数算到点集的头上去。怎么办呢?发现 实际上是可以以连通块为单位去计算的。如果每个连通块的容斥系数定义为在该连通块点集的导出子图中选边且能使该点集连通的所有方案的 之和,则把每个连通块的容斥系数乘起来恰好就是“形成了这种连通块分布状态的钦定方案”的 的总和。
既然知道了容斥系数的定义,接下来的问题就是求每个点集的容斥系数了,这是经典状压 dp,我们从一个 处转移:。
最后就是 dp 了。由于最后代入 版本计算的时候要能知道每个(大小为奇)连通块内最小 分别是多少,所以设 表示已经确定了 点集,其所有奇连通块的 最小值的点构成集合 ,所有方案的各连通块容斥系数之积的总和(对于新加偶连通块,由于最后统计答案时直接忽略了它,所以它的贡献 需要直接乘上,而不是只乘容斥系数)。暴力枚举新加的一个连通块点集来转移。为了避免算重,我们需要保证新加的连通块的 lowbit 比 的 lowbit 小。
复杂度是 的。(复杂度分析是这样的,由于过程是 for(s,1,U)for(t⊂s)for(T⊂U^S)
,后面两部分合起来相当于枚举了一个 的状态。)需要卡常卡常再卡常,我没卡过去。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=16,M=N*N/2,mod=998244353; int n,m,ans,id[N],_i2[66],dp[N][2][2],tmpb[N][2],coef[1<<15],siz[1<<15],mna[1<<15]; ll ta[N]; unordered_map<int,int>f[1<<15]; vector<int>vec[1<<15]; bool edgeempty[1<<15]; ll c,a[N],aa[N]; struct LiMiT {int u,v;}lim[M]; inline void add(int &x,int y){ (x+=y)>=mod&&(x-=mod); } inline int solve(int n,ll a[]){ int ans=0; for(int b=60;~b;b--){ if(!b){ tmpb[0][0]=1; for(int i=1;i<=n;i++){ tmpb[i][0]=tmpb[i][1]=0; if(a[i]&1)tmpb[i][0]+=tmpb[i-1][1],tmpb[i][1]+=tmpb[i-1][0]; tmpb[i][0]+=tmpb[i-1][0],tmpb[i][1]+=tmpb[i-1][1]; } ans+=tmpb[n][c&1]; break; } dp[0][0][0]=1; for(int i=1;i<=n;i++){ for(int j=0;j<=1;j++)for(int k=0;k<=1;k++) dp[i][j][k]=0; for(int j=0;j<=1;j++)for(int k=0;k<=1;k++){ if(a[i]>>b&1){ dp[i][j^1][k]=(dp[i][j^1][k]+1ll*dp[i-1][j][k]*((a[i]&((1ll<<b)-1))%mod+1))%mod; dp[i][j][k|1]=(dp[i][j][k|1]+1ll*dp[i-1][j][k]*((1ll<<b)%mod))%mod; } else dp[i][j][k]=(dp[i][j][k]+1ll*dp[i-1][j][k]*((a[i]&((1ll<<b)-1))%mod+1))%mod; } } (ans+=1ll*dp[n][c>>b&1][1]*_i2[b]%mod)%=mod; ll Xor=0; for(int i=1;i<=n;i++)Xor^=(a[i]>>b&1);//cout<<ans<<','; if(Xor!=(c>>b&1))break; } return ans%mod; } int main(){ freopen("graph.in","r",stdin);freopen("graph.out","w",stdout); cin>>n>>m>>c; for(int i=0;i<(1<<n);i++)f[i].reserve(512),f[i].max_load_factor(0.37); ll mxai=0; for(int i=1;i<=n;i++)cin>>a[i],mxai=max(mxai,a[i]),aa[i]=a[i]%mod; for(int i=1;i<=m;i++)cin>>lim[i].u>>lim[i].v; _i2[0]=1;for(int i=1;i<=60;i++)_i2[i]=(mod+1ll)/2*_i2[i-1]%mod; for(int s=0;s<(1<<n);s++){ edgeempty[s]=1; for(int i=1;i<=m;i++)if((s>>(lim[i].u-1)&1)&&(s>>(lim[i].v-1)&1)){ edgeempty[s]=0;break; } } for(int s=1;s<(1<<n);s++){ int lb=s&-s; siz[s]=siz[s^lb]+1; ll val=1e18+7; for(int i=0;i<n;i++)if((s>>i&1)&&val>a[i+1])mna[s]=i,val=a[i+1]; if(s==lb){coef[s]=1;continue;} coef[s]=(edgeempty[s]?1:0); for(int t=(s-1)&s;t;t=(t-1)&s)if(t&lb){ if(edgeempty[s^t])(coef[s]+=mod-coef[t])>=mod&&(coef[s]-=mod); } } int U=(1<<n)-1; for(int s=1;s<=U;s++){ if(siz[s]&1)f[s][1<<mna[s]]=(f[s][1<<mna[s]]+coef[s])%mod; else f[s][0]=(f[s][0]+(ll)coef[s]*(aa[mna[s]+1]+1))%mod; int cs=U^s; for(int us=cs;us;us=(us-1)&cs)if(!(us&s)&&(us&-us)==((s|us)&-(s|us))){ if(!coef[us])continue; int S=s|us; if(siz[us]&1){ for(auto t:f[s]){ add(f[S][1<<mna[us]|t.first],(ll)t.second*coef[us]%mod); } } else { for(auto t:f[s]){ add(f[S][t.first],(ll)coef[us]*t.second%mod*(aa[mna[us]+1]+1)%mod); } } } } for(int s=0;s<(1<<n);s++){ int tot=0; for(int i=1;i<=n;i++)if(s>>(i-1)&1)ta[++tot]=a[i]; ans=(ans+1ll*f[U][s]*solve(tot,ta))%mod; } cout<<ans<<'\n'; return 0; }
D2T1
次请你:在树上找三个点 ,使得 (保证有解;有 SPJ)。
考试时写的直径上找点居然过了,赛时拍了几组就拍出问题来了,并没有赶完 cdq 分治。
输入每个 时求一个中心 ,表示三条线的交点。用 2 次 dfs(或者一次,我写的比较麻烦)求出每个点最长的三个分支(不能属于同一个邻居),记其最远端为 ,长度为 。则只需要找到 (不妨设 )的任意一个点,用 cdq 分治即可。
#include <bits/stdc++.h> using namespace std; inline int read(){ int x=0;char ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x; } void print(int x){ if(x/10)print(x/10); putchar(x%10+48); } const int N=2e5+5; int n,q,dfc,c[N],mxlen[N],idlen[N],dep[N],dy[N][3],Ans[N],p1[N],p2[N],p3[N],Uo[N],Vo[N],Wo[N],fa[N][19],dfn[N],out[N]; bool ban[N]; vector<int>G[N],gg[N],suf[N],sufid[N]; struct node { int a,b,c,id,res,type; }tmp[N*2],a[N*2]; void init(int x,int p){ fa[x][0]=p;dfn[x]=++dfc; for(int i=1;i<=18;i++)fa[x][i]=fa[fa[x][i-1]][i-1]; for(int y:gg[x])if(y^p){ init(y,x); G[x].emplace_back(y); }out[x]=dfc; } void dfs0(int x,int p){ dep[x]=dep[p]+1; mxlen[x]=dep[x],idlen[x]=x; suf[x].resize(G[x].size()+1),sufid[x].resize(G[x].size()+1); suf[x][G[x].size()]=dep[x],sufid[x][G[x].size()]=x; for(int i=(int)G[x].size()-1;i>=0;i--){ int y=G[x][i]; dfs0(y,x); if(mxlen[x]<mxlen[y])mxlen[x]=mxlen[y],idlen[x]=idlen[y]; } int Mx=dep[x],Id=x; for(int i=(int)G[x].size()-1;i>=0;i--){ int y=G[x][i]; if(Mx<mxlen[y])Mx=mxlen[y],Id=idlen[y]; suf[x][i]=Mx,sufid[x][i]=Id; } } void dfs(int x,int p,int chuanlen,int chuanid){ int pre=dep[x],preid=x; for(int i=0;i<G[x].size();i++){ int y=G[x][i]; if(pre>suf[x][i+1]){ if(pre-dep[x]>chuanlen)dfs(y,x,pre-dep[x]+1,preid); else dfs(y,x,chuanlen+1,chuanid); } else { if(suf[x][i+1]-dep[x]>chuanlen)dfs(y,x,suf[x][i+1]-dep[x]+1,sufid[x][i+1]); else dfs(y,x,chuanlen+1,chuanid); } if(pre<mxlen[y])pre=mxlen[y],preid=idlen[y]; } vector<pair<int,int> >vec; for(int y:G[x])if(y^p)vec.emplace_back(mxlen[y]-dep[x],idlen[y]); vec.emplace_back(chuanlen,chuanid); sort(vec.begin(),vec.end(),greater<pair<int,int> >()); while(vec.size()<3)vec.emplace_back(0,x); a[x].a=vec[0].first,a[x].b=vec[1].first,a[x].c=vec[2].first; p1[x]=vec[0].second,p2[x]=vec[1].second,p3[x]=vec[2].second; } void add(int x,int y){ for(;x<=n;x+=x&-x)c[x]=max(c[x],y); } int ask(int x){ int s=0; for(;x;x-=x&-x)s=max(s,c[x]);//cerr<<'\n'; return s; } void del(int x){ for(;x<=n;x+=x&-x)c[x]=0; } bool cmp(node i,node j){ return i.b<j.b; } void cdq(int l,int r){ if(l==r)return; int mid=l+r>>1; cdq(l,mid),cdq(mid+1,r); int j=r+1; for(int i=mid;i>=l;i--){ while(j>=mid+2&&a[j-1].b>=a[i].b){ j--; if(a[j].type)add(n-a[j].c,a[j].id); } if(a[i].type==0)a[i].res=max(a[i].res,ask(n-a[i].c)); // if((a[i].a==4&&a[i].b==1&&a[i].c==1)&&a[i].type==0&&a[i].id==14){cerr<<'.'; // for(int k=j;k<=r;k++)if(a[k].type==1)cerr<<a[k].a<<' '<<a[k].b<<' '<<a[k].id<<';'; // cerr<<a[i].c<<'<'; // } } for(;j<=r;j++)if(a[j].type)del(n-a[j].c); int xx=l,yy=mid+1,zz=l; while(xx<=mid&&yy<=r){ if(a[xx].b<a[yy].b)tmp[zz++]=a[xx++]; else tmp[zz++]=a[yy++]; } while(xx<=mid)tmp[zz++]=a[xx++];while(yy<=r)tmp[zz++]=a[yy++]; for(int i=l;i<=r;i++)a[i]=tmp[i]; // if(r-l+1>100)cerr<<l<<' '<<r<<','; } int findfa(int x,int len){ int y=x; for(int i=18;~i;i--)if(dep[x]-dep[fa[y][i]]<=len)y=fa[y][i]; return y; } int glca(int u,int v){ if(u==v)return u; if(dep[u]>dep[v])swap(u,v); for(int i=18;~i;i--)if(dep[fa[v][i]]>=dep[u])v=fa[v][i]; if(u==v)return u; for(int i=18;~i;i--)if(fa[u][i]!=fa[v][i])u=fa[u][i],v=fa[v][i]; return fa[u][0]; } int main(){ freopen("game.in","r",stdin);freopen("game.out","w",stdout); n=read(); for(int i=1,u,v;i<n;i++){ u=read(),v=read(); gg[u].emplace_back(v),gg[v].emplace_back(u); } init(1,0); dep[0]=-1; dfs0(1,0); dfs(1,0,0,1); int m=n; for(int i=1;i<=n;i++)a[i].id=i,a[i].type=1; q=read(); for(int x,y,z,o=1;o<=q;o++){ x=read(),y=read(),z=read(); int uo=(x+y-z)/2,vo=(x-y+z)/2,wo=(z-x+y)/2; dy[o][0]=0,dy[o][1]=1,dy[o][2]=2; if(uo<vo)swap(uo,vo),swap(dy[o][0],dy[o][1]);if(uo<wo)swap(uo,wo),swap(dy[o][0],dy[o][2]);if(vo<wo)swap(vo,wo),swap(dy[o][1],dy[o][2]); m++,a[m].a=uo,a[m].b=vo,a[m].c=wo; a[m].id=o; Uo[o]=uo,Vo[o]=vo,Wo[o]=wo; } // for(int i=1;i<=n;i++)cerr<<a[i].a<<' '<<a[i].b<<' '<<a[i].c<<",\n"; sort(a+1,a+m+1,[](node a,node b){return a.a==b.a?a.b==b.b?a.c==b.c?a.type<b.type:a.c<b.c:a.b<b.b:a.a<b.a;}); cdq(1,m); for(int i=1;i<=m;i++)if(a[i].type==0)Ans[a[i].id]=a[i].res; for(int i=1;i<=q;i++){ int cc=Ans[i]; // cerr<<cc<<'>'; // cerr<<cc<<' '<<Uo[cc]<<' '<<Vo[cc]<<' '<<Wo[cc]<<";\n"; int au,av,aw; if(dfn[p1[cc]]>=dfn[cc]&&dfn[p1[cc]]<=out[cc])au=findfa(p1[cc],dep[p1[cc]]-dep[cc]-Uo[i]); else { int lca=glca(p1[cc],cc);//cout<<dep[7]<<']'; if(dep[cc]-dep[lca]>=Uo[i])au=findfa(cc,Uo[i]); else au=findfa(p1[cc],dep[p1[cc]]+dep[cc]-2*dep[lca]-Uo[i]); } if(dfn[p2[cc]]>=dfn[cc]&&dfn[p2[cc]]<=out[cc])av=findfa(p2[cc],dep[p2[cc]]-dep[cc]-Vo[i]); else { int lca=glca(p2[cc],cc); if(dep[cc]-dep[lca]>=Vo[i])av=findfa(cc,Vo[i]); else av=findfa(p2[cc],dep[p2[cc]]+dep[cc]-2*dep[lca]-Vo[i]); } if(dfn[p3[cc]]>=dfn[cc]&&dfn[p3[cc]]<=out[cc])aw=findfa(p3[cc],dep[p3[cc]]-dep[cc]-Wo[i]); else { int lca=glca(p3[cc],cc); if(dep[cc]-dep[lca]>=Wo[i])aw=findfa(cc,Wo[i]); else aw=findfa(p3[cc],dep[p3[cc]]+dep[cc]-2*dep[lca]-Wo[i]); } if(dy[i][0]==0)print(au);else if(dy[i][1]==0)print(av);else if(dy[i][2]==0)print(aw);putchar(' '); if(dy[i][0]==1)print(au);else if(dy[i][1]==1)print(av);else if(dy[i][2]==1)print(aw);putchar(' '); if(dy[i][0]==2)print(au);else if(dy[i][1]==2)print(av);else if(dy[i][2]==2)print(aw);putchar('\n'); } }
D2T2
给定 、一个初始值 和一个序列 ,现在进行 次操作,每次随一个 ,有 的概率 ,有 的概率 。一组 次操作的价值为 。对每个 ,求在 时的期望价值。
略。
D2T3
有一棵 个点的树和每个点的权值 , 次给定 ,询问 。其中 定义为两点之间简单路径的边数。
我们按层来看。记 为 这一排在 的前一个的点。一次询问所走的是 子树的上半部分,这部分可以看成是:先令 , 和 同步跳右儿子(最右边的儿子,如果为空呢?*),每次把 计入答案,其中 表示 所在层的 。
*:处理 表示 的最右儿子,它的求法是按照 dfs 序 dfs,实时维护每一层已经遍历的点的 vector,对于 ,先遍历完它的所有儿子,然后取 层 vector 的 作为 (这就是为空时应该去的地方)。
很显然这是对的,并且在没有儿子时也能跳到想去的地方。但是复杂度是 的。考虑用倍增优化它(“跳”通常都可以用倍增优化)。设 表示从 开始跳 个点(包括 )的过程中跳到的所有 个点的 。考虑递推:
只需预处理 表示 和 表示 纵向的后缀和(就是 )。
询问时倍增跳,同时做与递推时类似的事情即可。
#include <bits/stdc++.h> using namespace std; typedef long long ll; int poi; char buff[1024]; vector<char>buf; inline int read(){ int x=0;char ch=buf[poi++]; while(ch<'0'||ch>'9')ch=buf[poi++]; while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=buf[poi++]; return x; } const int N=1e6+5; int n,q,v[N],pa[N],to[N],fr[N],tiao[N][21],dep[N],peng[21][N][2],yule[21][N][2],pos[N]; ll f[N][21]; vector<int>vec[N],G[N]; void dfs(int x,int p){ dep[x]=dep[p]+1; vec[dep[x]].emplace_back(x); for(int y:G[x])if(y^p){ dfs(y,x); } if(vec[dep[x]+1].size())to[x]=vec[dep[x]+1].back(); } ll gg(int x,int K){ int y=x;int k=K; for(int i=20;~i;i--)if(k>=(1<<i)&&tiao[y][i])k-=1<<i,y=tiao[y][i]; int tar=y;//if(x==1&&K==4)cout<<tar<<','; ll ans=0; y=x; k=K; for(int i=20;~i;i--)if(k>=(1<<i)&&tiao[y][i])k-=1<<i,ans+=f[y][i]+(yule[i][tiao[y][i]][0]-yule[i][to[tar]][0] -yule[i][tiao[y][i]][1]+yule[i][to[tar]][1])*(1ll<<i),y=tiao[y][i];//cerr<<'\n'; ans+=f[tar][0]; return ans; } int main(){ freopen("tree.in","r",stdin);freopen("tree.out","w",stdout); if(FILE* fp=fopen("tree.in","r")){ while(size_t len=fread(buff,1,sizeof buff,fp)) buf.insert(buf.end(),buff,buff+len); fclose(fp); } n=read();//cerr<<'.'; for(int i=1;i<=n;i++)v[i]=read(); for(int i=2;i<=n;i++)pa[i]=read(),G[pa[i]].emplace_back(i); dep[0]=-1; dfs(1,0); for(int d=0;d<n;d++){ if(vec[d].empty())continue; for(int i=0;i<vec[d].size();i++){ pos[vec[d][i]]=i; for(int j=0;j<=20;j++)peng[j][vec[d][i]][v[vec[d][i]]>>j&1]++; if(i)for(int j=0;j<=20;j++)for(int b=0;b<=1;b++)peng[j][vec[d][i]][b]+=peng[j][vec[d][i-1]][b]; } } for(int d=n-1;d>=0;d--){ for(int i:vec[d]){ for(int j=0;j<=20;j++)for(int b=0;b<=1;b++) yule[j][i][b]=peng[j][i][b]+yule[j][to[i]][b]; } } for(int i=1;i<=n;i++)tiao[i][0]=to[i]; for(int j=1;j<=20;j++)for(int i=1;i<=n;i++){ tiao[i][j]=tiao[tiao[i][j-1]][j-1]; } for(int d=0;d<n;d++){ for(int i=0;i<vec[d].size();i++){ f[vec[d][i]][0]=v[vec[d][i]]; if(i)f[vec[d][i]][0]+=f[vec[d][i-1]][0]; } } for(int j=1;j<=20;j++)for(int i=1;i<=n;i++){ f[i][j]=f[i][j-1]+f[tiao[i][j-1]][j-1]+(yule[j-1][tiao[i][j-1]][0]-yule[j-1][tiao[i][j]][0] -yule[j-1][tiao[i][j-1]][1]+yule[j-1][tiao[i][j]][1])*(1ll<<j-1); } fr[1]=0,to[0]=1; for(int i=1;i<=n;i++)if(to[i])fr[to[i]]=i; q=read(); while(q--){ int x=read(),K=read(); cout<<gg(x,K)-(pos[x]?gg(vec[dep[x]][pos[x]-1],K):0)<<'\n'; } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效