noip模拟1
A 追逐游戏 (chase)
后悔没做玮给的那道题了。。
就是分讨,有两种情况。
第一种是那个人先到终点,追的人追不上,这样答案就是追的人到终点的距离,在终点追上。
第二种是追的人先被追的人的毕经之路上(当然也可以一开始就在必经之路上),这样等效于一条从 的树链,被追的人走 步。
那这个就是类似于树上走 步的问题了,和上面那道题完全一样。
点击查看代码
#include<bits/stdc++.h> using namespace std; int n,q; const int N=1e6+5; vector<int>e[N]; int dep[N],siz[N],son[N],id[N],fa[N],rk[N]; void dfs1(int u,int f) { // cout<<u<<" "; fa[u]=f,dep[u]=dep[f]+1,siz[u]=1; for(int v:e[u]) { if(v==f) continue; dfs1(v,u), siz[u]+=siz[v]; if(siz[v]>siz[son[u]]) son[u]=v; } } int tim,top[N]; void dfs2(int u,int t) { top[u]=t,id[u]=++tim,rk[tim]=u; if(son[u]) dfs2(son[u],t); for(int v:e[u]) { if(v==fa[u]||v==son[u]) continue; dfs2(v,v); } } int lca(int u,int v) { while(top[u]!=top[v]) { if(dep[top[u]]<dep[top[v]]) swap(u,v); u=fa[top[u]]; } if(dep[u]>dep[v]) swap(u,v); return u; } inline int dis(int u,int v) { return dep[u]+dep[v]-2*dep[lca(u,v)]; } bool tag[N]; int jmp(int x,int k) { while(114) { if(dep[x]-dep[top[x]]+1>k) break; k-=dep[x]-dep[top[x]]+1; x=fa[top[x]]; }return rk[id[x]-k]; } int s; signed main() { ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n>>s>>q; for(int i=1;i<n;i++) { int u,v;cin>>u>>v; e[u].push_back(v),e[v].push_back(u); } dfs1(1,0),dfs2(1,1); while(q--) { int d,x; cin>>x>>d; if(dis(s,x)<d) { s=x; cout<<s<<" ";continue; } int lc=lca(x,s); int dis1=dis(s,lc),dis2=dis(x,lc); if(dis1>d) s=jmp(s,d); else s=jmp(x,dis2-(d-dis1)); cout<<s<<" "; } }
B 统计
这个题很神。考虑一个区间是符合要求的,可以简单地想到区间内元素的和是 的倍数。
但是由于哈希冲突的类似问题,会有些许非法序列也符合这样的条件,那解决方法很简单。
考虑像 一样引入一个东西叫做 ,对于值域内的所有数,我们给他 一个 作为他的映射。
这样就能有效避免非法多算的情况。
复杂度是 ,有 分。
进一步地,我们进行参照的和是 ,每次对于一个序列的前缀和去判断是否能被它整除,也就是:
如果我们能让 ,那我们的判断条件就成了 也就是 了。
这样只需要统计出有多少个前缀和相等即可,每一个相等的前缀和的贡献都是 的。
那么最简单的方法,我们令 即可解决。
时间复杂度
点击查看代码
#include<bits/stdc++.h> using namespace std; #define int long long #define rnd rand int m,T,n; const int N=1e6+6; int a[N]; long long val[N]; long long sum[N]; signed main() { freopen("st.in","r",stdin); freopen("st.out","w",stdout); mt19937 rnd(time(0)); // srand(time(NULL)); ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>T; while(T--) { cin>>n>>m; int ans=0; for(int i=1;i<=n;i++) cin>>a[i]; int s=0; for(int i=1;i<m;i++) val[i]=rand(),s+=val[i]; val[m]=-s; for(int i=1;i<=n;i++) sum[i]=(sum[i-1]+val[a[i]]); sort(sum,sum+1+n); int cnt=1; for(int i=1;i<=n;i++) { if(sum[i]!=sum[i-1]) cnt=0;ans+=cnt;cnt++; } cout<<ans<<"\n"; } return 0; }
C 软件工程
额,贪心即可。
考虑每个线段的分布很离♂ 散,那么我们就可以贪心地保留尽可能多的单个线段,因为单个线段的交是它本身。
并且,两个线段的交一定小于等于其中后任意一个线段。
那么,我们找前 个最大的线段,让它们单独成一个集合,剩下一个集合把其他线段扔进去,能拿到近乎满分。
若要满分,第一个点跑个暴力深搜就好了。
点击查看代码
#include<bits/stdc++.h> #define int long long #define N 5005 #define M 14 using namespace std; int n,k; struct Seg{ int l,r; bool operator<(const Seg &x)const{ return r-l+1>x.r-x.l+1; } }Sg[N]; int ans; const int inf=1e18; int p[N]; vector<int>t[N]; int x[N],y[N]; void dfs(int stp) { if(stp==n+1) { int res=0; for(int i=1;i<=k;i++)t[i].clear(); for(int i=1;i<=n;i++) t[p[i]].push_back(i); for(int i=1;i<=k;i++) { if(!t[i].size())continue; int tmp=t[i][0]; int jx=x[tmp],jy=y[tmp]; for(int j:t[i]) { if(j==t[i][0]) continue; if(jx<x[j]) { if(jy<x[j]) { jx=jy=0;break; } else jx=x[j],jy=min(jy,y[j]); } else { if(jx>y[j]) { jx=jy=0;break; } else jy=min(jy,y[j]); } } res+=max(0ll,jy-jx); } ans=max(ans,res); return ; } for(int i=1;i<=k;i++) { p[stp]=i; dfs(stp+1); } } signed main() { freopen("se.in","r",stdin); freopen("se.out","w",stdout); scanf("%lld%lld",&n,&k); if(n<=10) { for(int i=1;i<=n;i++)scanf("%lld%lld",&x[i],&y[i]); dfs(1); cout<<ans; return 0; } for(int i=1,l,r;i<=n;i++) { scanf("%lld%lld",&l,&r); Sg[i]=(Seg){l,r}; } sort(Sg+1,Sg+1+n); for(int i=1;i<k;i++)ans+=Sg[i].r-Sg[i].l; int l=-inf,r=inf; for(int i=k;i<=n;i++) { l=max(l,Sg[i].l); r=min(r,Sg[i].r); } ans+=l>r?0:r-l; printf("%lld",ans); return 0; }
D 命运的X
脑电波题,不太可想象。
最终答案是 ,其中 是 数组的 Border 集合。
点击查看代码
#include<bits/stdc++.h> using namespace std; #define int long long const int mod=998244353; int ppow(int a,int b) { int res=1; while(b) { if(b&1) res=(res*a)%mod; a=(a*a)%mod,b>>=1; }return res; } const int N=2e5+5; int n,m,a[N]; int nxt[N]; signed main() { freopen("x.in","r",stdin); freopen("x.out","w",stdout); int T;cin>>T; while(T--) { cin>>m>>n; for(int i=0;i<n;i++) cin>>a[i]; memset(nxt,0,sizeof(nxt)); int j=0; nxt[1]=0; for(int i=2;i<=n;i++) { while(j&&a[i]!=a[j+1]) j=nxt[j]; if(a[j+1]==a[i])j++; nxt[i]=j; } int ans=ppow(m,n); int now=nxt[n]; while(now) ans=(ans+ppow(m,now))%mod,now=nxt[now]; cout<<ans%mod<<"\n"; } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!