个人训练题解-夏季3-International Odoo Programming Contest 2024
前言:某些人抄题解就算了还故意写一篇文章出来说写的像是以前就写过类似做法。一个人线段树会写起来完全不一样的好几种写法吗?cf和个人训练和线下赛完全不是一个水平,还有代打经历,代了一个div2 rk1 什么成分我不说了。线下赛看看实力吧。
持续更新 也可以催我补()
F.Odoo Trees
题解貌似是对操作建一棵树然后树上二分? 这里提供一种更直观的整体二分的做法。如果您不知道什么是整体二分 可以去学习一下P3527这道题 感觉这道题是非常好的整体二分入门题(当然我其实并不太会这个算法
整体二分可以处理:二分这个询问到底是第几个操作的时候变好的
k的大小为1e9 我们知道1e9最多只有10个不同的素数相乘得到,最多有31个素数(可相等)相乘得到,我们考虑二分的复杂度为logn 树上数组加的复杂度是logn 加的次数最多是10次 所以是10logn*logn*n 应该能过(擦汗
预处理部分:
因为被k整除要让与k相同的因子达到至少与k一样 我们先对k分解质因数,并且计算质因数数量,此时我们得到(最多)10个num ,然后让后面的所有乘操作都去一个一个除这些num 计算后面这些操作乘一次能让贡献增加多少,这样复杂度不高,化乘为加
整体二分:
枚举操作到的次数 mid 把l-mid操作都做一遍 就是分那10个num 然后建10课树状数组,分别做区间加,然后比较现在手上拿着的询问,哪些是满足的 哪些是不满足的,满足的丢到左边,不满足的丢掉右边,此时要做右边的时候,我们考虑减少操作次数 :右边的肯定是l-mid操作做完了还不够的 我们之后对右边操作的时候就不要再加左边的操作次数了 这里直接加上 这样就符合log了(类似树上二分时候的思维
做操作的时候 因为是对子树操作的 我们把树做一个dfs序 这样就是对连续的序列操作了
代码

#include<bits/stdc++.h> #define close std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) using namespace std; typedef long long ll; const ll MAXN = 2e5+7; const ll mod = 1e9+7; const ll inf = 0x3f3f3f3f; vector<int> fac; int a[MAXN]; int C[MAXN][10]; int t[MAXN][10]; int opv[MAXN][10]; int opt[MAXN]; int dfn[MAXN],rdfn[MAXN],rak[MAXN],dft; int ans[MAXN]; int prime[10],cnt[10],tot; vector<int> adj[MAXN]; int p[MAXN],p1[MAXN],p2[MAXN]; void divide_fac(ll x) { ll tm=x; for(int i=2; (ll)i*i<=tm; i++) { while(x%i==0) { fac.push_back(i); x/=i; } } if(x!=1){ fac.push_back(x); } } void dfs(int u,int fa) { dfn[u]=++dft; rak[dft]=u; for(auto &v:adj[u]) { if(v==fa) continue; dfs(v,u); } rdfn[u]=dft; } int lowbit(int x) { return x&-x; } void addv(int x,int val,int k) { while(x<=MAXN-5) { t[x][k]+=val; x+=lowbit(x); } } ll getsum(int x,int k) { ll sum=0; while(x) { sum+=t[x][k]; x-=lowbit(x); } return sum; } void calc(int l,int r,int ql,int qr) { if(ql>qr) return ; int mid=l+r>>1; if(l==r) { for(int i=ql; i<=qr; i++) ans[rak[p[i]]]=l; return; } int cnt1=0,cnt2=0; for(int k=1; k<=tot; k++) { for(int i=l; i<=mid; i++) { addv(dfn[opt[i]],opv[i][k],k); addv(rdfn[opt[i]]+1,opv[i][k]*-1,k); } } for(int i=ql; i<=qr; i++) { int flag=1; for(int k=1; k<=tot; k++) { int tmp=getsum(p[i],k); if(tmp+C[p[i]][k]<cnt[k]) { flag=0; break; } } if(!flag) { for(int k=1; k<=tot; k++) { int tmp=getsum(p[i],k); C[p[i]][k]+=tmp; } p2[++cnt2]=p[i]; } else { p1[++cnt1]=p[i]; } } for(int i=ql; i<=ql+cnt1-1; i++) { p[i]=p1[i-ql+1]; } for(int i=ql+cnt1; i<=qr; i++) { p[i]=p2[i-ql-cnt1+1]; } for(int k=1; k<=tot; k++) { for(int i=l; i<=mid; i++) { addv(dfn[opt[i]],-opv[i][k],k); addv(rdfn[opt[i]]+1,opv[i][k],k); } } calc(l,mid,ql,ql+cnt1-1); calc(mid+1,r,ql+cnt1,qr); } int ab[MAXN]; void solve() { int n,k,q; cin>>n>>k>>q; divide_fac(k); sort(fac.begin(),fac.end()); for(auto &it:fac) { if(it!=prime[tot]) { tot++; prime[tot]=it; cnt[tot]=1; } else cnt[tot]++; } for(int i=1; i<=n; i++) { cin>>a[i]; ab[i]=a[i]; } for(int i=2; i<=n; i++) { int fa; cin>>fa; adj[fa].push_back(i); } dfs(1,0); for(int i=1; i<=n; i++) { a[i]=__gcd(a[i],k); for(int j=1; j<=tot; j++) { while(a[i]%prime[j]==0) { C[dfn[i]][j]++; a[i]/=prime[j]; } } } for(int i=1; i<=q; i++) { cin>>opt[i]; int val; cin>>val; val=__gcd(val,k); for(int j=1; j<=tot; j++) { while(val%prime[j]==0) { opv[i][j]++; val/=prime[j]; } } } opt[q+1]=1; for(int i=1; i<=n; i++) p[i]=i; calc(1,q+1,1,n); for(int i=1; i<=n; i++) { if(ab[i]%k==0) ans[i]=0; } for(int i=1; i<=n; i++) { if(ans[i]==q+1) cout<<"-1 "; else cout<<ans[i]<<" "; } } signed main() { close; solve(); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现