NOIP 2024 题解
NOIP 2024 题解
各题的 AC 代码放在了文末。
T1
首先对于两个串都不能动的位置,直接统计是否相等。
对于连续的一段能动的位置,这一段的数可以随便交换,可以预处理每个位置属于哪一段,以及这一段中 0 和 1 的个数。
我们贪心地考虑,优先匹配一个串能动,另一个串不能动的位置。可以感受到,先把不能动的位置匹配掉后,剩下的位置是两个串都可以随便移动的,剩下的位置限制会更松,于是这样是比较优的。
最后处理都能动的位置,我们贪心地能匹配就匹配,因为匹配和失配的贡献都是 1,所以与匹配的顺序无关。
时间复杂度
T2
按照确定的点分段,考虑到每一段都是独立的。
考虑求每一段合法的方案数,正难则反,用总数减去不合法的方案数。
不合法的方案数就是设左边值为
这就要求这一段前后连起来,且最后一个值不为
再考虑开头的和结尾的两段,发现怎么取都是合法的。
我们需要快速幂,时间复杂度
T3
前言:
怎么了呢?是不敢面对自己的分数吗,到了元旦才想起来要改这道题。
赛时想到了对每个叶子间的路径计数,如果路径上有关键边那么就是可计数的,对于路径上的方案计数,同时对每个挂在路径上的树的方案计数。可惜我的式子是一个 DP 的形式,我并没有想到式子是可以直接根据各节点的度数计算。
言归正传。
首先如果
其中
而如果
我们称使生成树合法的关键边为有用的关键边,我们把有用的关键边取出来,那么它的两端生成树上连的边,会一直延伸到两个原树上的叶子节点。
我们可以对包含关键边的叶子节点间的路径计数,对于这条链上挂着的子树,和
其中
其中
对于
时间复杂度
T4
考虑枚举长为
一段的 LCA 就是每次加一个点求 LCA,也可以是合并两个子区间的 LCA。
可以考虑线段树上合并区间 LCA,每次合并用倍增实现
可以用 ST 表代替线段树,预处理 ST 表合并时可以倍增
另外,也可以求区间欧拉序的最小最大值,类似欧拉序两个点的 LCA,求 RMQ,这样也能做到
性质 B
上面的做法可以直接通过性质 B。
性质 A
考虑整体二分,就要实现一个数据结构,支持加入或删除一个点,然后查询区间内最长连续段的长度是否大于等于
可以用线段树实现,时间复杂度
如果有强大的观察能力或联想能力,可以发现一段区间的 LCA 的深度就是相邻两个点的 LCA 深度的最小值,即
证明就是,考虑这一段区间的 LCA,这段区间的点分布在 LCA 的不同子树内,则一定有
于是就转化为了性质 A。
考虑对于每个点
设询问区间为
分类讨论,当
当
前者对
具体地,把做扫描线的量排序,然后双指针把另一个量加入数据结构中或在数据结构中查询。
前者
当
时间复杂度
T1:
const int N=1e5+5; char a[N],b[N],c[N],d[N]; int m1[N],m2[N]; int t1,t2; int c1[N],c2[N],c3[N],c4[N]; signed main(){ // freopen("edit.in","r",stdin); // freopen("edit.out","w",stdout); read(T); while(T--){ read(n); scanf("%s%s%s%s",a+1,b+1,c+1,d+1); int ans=0; t1=1,t2=1; c1[1]=c2[1]=c3[1]=c4[1]=0; fo(i,1,n){ if(c[i]=='1')m1[i]=t1,c1[t1]+=a[i]=='1',c2[t1]+=a[i]=='0'; else ++t1,m1[i]=c1[t1]=c2[t1]=0; if(d[i]=='1')m2[i]=t2,c3[t2]+=b[i]=='1',c4[t2]+=b[i]=='0'; else ++t2,m2[i]=c3[t2]=c4[t2]=0; } fo(i,1,n){ int j=m1[i],k=m2[i]; if(c[i]=='0'&&d[i]=='0')ans+=a[i]==b[i]; else if(c[i]=='0'){ if(a[i]=='0'&&c4[k])c4[k]--,ans++; if(a[i]=='1'&&c3[k])c3[k]--,ans++; } else if(d[i]=='0'){ if(b[i]=='1'&&c1[j])c1[j]--,ans++; if(b[i]=='0'&&c2[j])c2[j]--,ans++; } } fo(i,1,n){ int j=m1[i],k=m2[i]; if(c[i]=='1'&&d[i]=='1'){ if(c1[j]&&c3[k])c1[j]--,c3[k]--,ans++; else if(c2[j]&&c4[k])c2[j]--,c4[k]--,ans++; } } write(ans,'\n'); } return 0; }
T2:
signed main(){ // freopen("assign.in","r",stdin); // freopen("assign.out","w",stdout); read(T); while(T--){ read(n,m,V); fo(i,1,m){ read(a[i].first,a[i].second); } sort(a+1,a+1+m); int flag=0; fo(i,1,m-1){ if(a[i].first==a[i+1].first&&a[i].second!=a[i+1].second)flag=1; } if(flag){ write("0\n"); continue; } m=unique(a+1,a+1+m)-a-1; int ans=1; fo(i,1,m-1){ int len=a[i+1].first-a[i].first; ans=(ll)ans*(pow1(V,2*len)-(ll)pow1(V,len-1)*(V-1)%mod)%mod; } ans=(ll)ans*pow1(V,2*(a[1].first-1))%mod*pow1(V,2*(n-a[m].first))%mod; write((ans+mod)%mod,'\n'); } return 0; }
T3:
const int mod=1e9+7; const int N=1e5+5; vp g[N]; int cid,T,n,K,key[N],deg[N]; int pow1(int x,int y){ int res=1; for(;y;y>>=1,x=(ll)x*x%mod)if(y&1)res=(ll)res*x%mod; return res; } int sum,f1[N],f2[N]; void dfs(int x,int y) { int flag=0; f1[x]=f2[x]=0; int s1=0,s2=0; for(auto v:g[x]) { if(v.first==y) continue; flag=1; dfs(v.first,x); int t1=f1[v.first],t2=f2[v.first]; if(key[v.second])t1=(t1+t2)%mod,t2=0; sum=(sum+(ll)s1*(t1+t2)%mod*pow1(deg[x]-1,mod-2))%mod; sum=(sum+(ll)s2*(t1)%mod*pow1(deg[x]-1,mod-2))%mod; s1=(s1+t1)%mod; s2=(s2+t2)%mod; f2[x]=(f2[x]+(ll)t2*pow1(deg[x]-1,mod-2))%mod; f1[x]=(f1[x]+(ll)t1*pow1(deg[x]-1,mod-2))%mod; } if(!flag) { f1[x]=0,f2[x]=1; } } int fac[N]; signed main(){ usefile("traverse"); read(cid,T); while(T--) { read(n,K); fo(i,1,n)deg[i]=0,g[i].clear(); fu(i,1,n){ int u,v; read(u,v); g[u].pb({v,i}); g[v].pb({u,i}); deg[u]++,deg[v]++; key[i]=0; } fo(i,1,K){ int e; read(e); key[e]=1; } if(n==2) {write("1\n"); continue;}; int prod=1; fac[0]=1; fo(i,1,n)fac[i]=(ll)fac[i-1]*i%mod; fo(i,1,n)prod=(ll)prod*fac[deg[i]-1]%mod; fo(i,1,n)if(deg[i]!=1) { sum=0; dfs(i,0); write((ll)prod*sum%mod,'\n'); break; } } return 0; }
T4:
const int N=5e5+5; int n,Q; vector<int> g[N]; int fa[N][19]; int dep[N]; int lca(int x,int y){ if(dep[x]<dep[y])swap(x,y); for(int i=dep[x]-dep[y],j=0;i;i>>=1,j++)if(i&1)x=fa[x][j]; if(x==y)return x; fd(i,18,0)if(fa[x][i]!=fa[y][i]){ x=fa[x][i],y=fa[y][i]; } return fa[x][0]; } void dfs(int x,int y){ fa[x][0]=y; dep[x]=dep[y]+1; fo(i,1,18)fa[x][i]=fa[fa[x][i-1]][i-1]; for(int v:g[x]){ if(v!=y)dfs(v,x); } } int ans[N]; int v[N]; struct arr{ int l,r,k,num; }a[N],q[N]; stack<pair<int,int> > stk; struct BIT{ int lowbit(int x){ return x&(-x); } int s[N]; void update(int x,int y){ while(x<n)s[x]=max(s[x],y),x+=lowbit(x); } int query(int x){ int _s=0; while(x)_s=max(_s,s[x]),x-=lowbit(x); return _s; } }b; struct tree{ #define ls (x<<1) #define rs (ls|1) #define mid ((l+r)>>1) int s[N*4]; void update(int x,int l,int r,int y,int z){ if(l==r){ s[x]=max(s[x],z); return; } if(y<=mid)update(ls,l,mid,y,z); else update(rs,mid+1,r,y,z); s[x]=max(s[ls],s[rs]); } int query(int x,int l,int r,int L,int R){ if(L<=l&&r<=R)return s[x]; if(R<l||r<L)return 0; return max(query(ls,l,mid,L,R),query(rs,mid+1,r,L,R)); } }tr; struct ST{ int mx[19][N]; void make(){ fo(i,1,n)mx[0][i]=dep[i]; fo(i,1,__lg(n)){ fo(j,1,n-(1<<i)+1){ mx[i][j]=max(mx[i-1][j],mx[i-1][j+(1<<i-1)]); } } } int get(int l,int r){ int len=r-l+1; return max(mx[__lg(len)][l],mx[__lg(len)][r-(1<<__lg(len))+1]); } }ST; signed main(){ // freopen("query.in","r",stdin); // freopen("query.out","w",stdout); read(n); fo(i,1,n-1){ int u,v; read(u,v); g[u].push_back(v); g[v].push_back(u); } dfs(1,0); fo(i,1,n-1){ v[i]=dep[lca(i,i+1)]; while(stk.size()&&v[i]<=stk.top().first)stk.pop(); if(stk.size())a[i].l=stk.top().second+1; else a[i].l=1; stk.push({v[i],i}); a[i].k=v[i]; } while(stk.size())stk.pop(); fd(i,n-1,1){ while(stk.size()&&v[i]<=stk.top().first)stk.pop(); if(stk.size())a[i].r=stk.top().second-1; else a[i].r=n-1; stk.push({v[i],i}); } sort(a+1,a+n,[](arr x,arr y){ return x.r>y.r; }); ST.make(); read(Q); fo(i,1,Q){ read(q[i].l),read(q[i].r),read(q[i].k); q[i].num=i; if(q[i].k==1){ ans[i]=ST.get(q[i].l,q[i].r); } q[i].r--; q[i].k--; } sort(q+1,q+1+Q,[](arr x,arr y){ return x.r>y.r; }); int st=1; fo(i,1,n-1){ while(st<=Q&&q[st].r>a[i].r){ if(!q[st].k){ ++st; continue; } ans[q[st].num]=max(ans[q[st].num],b.query(q[st].r-q[st].k+1)); ++st; } b.update(a[i].l,a[i].k); } while(st<=Q){ if(!q[st].k){ ++st; continue; } ans[q[st].num]=max(ans[q[st].num],b.query(q[st].r-q[st].k+1)); ++st; } sort(a+1,a+n,[](arr x,arr y){ return x.r-x.l+1>y.r-y.l+1; }); st=1; sort(q+1,q+1+Q,[](arr x,arr y){ return x.k>y.k; }); fo(i,1,n-1){ while(st<=Q&&q[st].k>a[i].r-a[i].l+1){ if(!q[st].k){ ++st; continue; } ans[q[st].num]=max(ans[q[st].num],tr.query(1,1,n-1,q[st].l+q[st].k-1,q[st].r)); ++st; } tr.update(1,1,n-1,a[i].r,a[i].k); } while(st<=Q){ if(!q[st].k){ ++st; continue; } ans[q[st].num]=max(ans[q[st].num],tr.query(1,1,n-1,q[st].l+q[st].k-1,q[st].r)); ++st; } fo(i,1,Q)write(ans[i],'\n'); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下