2022“杭电杯”中国大学生算法设计超级联赛(2)

比赛链接

2022“杭电杯”中国大学生算法设计超级联赛(1)

7150. Static Query on Tree

给定一棵 n 个节点的树,每个节点只能向根节点走。 q 个询问,每次给定三个集合 ABC ,在 ABC 中分别取出 x,y,z ,问 x,y 往最终在 z 汇合, x,y 都经过的点有多少个

解题思路

树链剖分+线段树

AB 中的点到根节点的路径打上标记,C 的子树的所有点打上标记,查询有三种标记的节点即为答案
可用 1,2,4 分别表示三种标记,维护区间与和区间或的信息,打上懒标记,每次询问查询区间与是否等于 7,同时每次查询后还需要一个区间清除的懒标记

  • 时间复杂度:O(nlog2n)

另外一种线段树写法:将所有的 07 的状态记录下来,线段树维护这些信息,每当加入一个状态时,线段树区间节点状态发生相应变化,设置 ABC 三种标记的位为 0,1,2,例如对于不含 A 的状态有 0,2,4,6,则加入 A 后的新状态为 1,3,5,7,同时最后需要清除旧状态

  • 时间复杂度:(12nlog2logn)

树链剖分+区间问题

用树链剖分将每个标记的区间提取出来后,分别将这些区间进行合并,最后求这三种区间的交集即可

  • 时间复杂度:O(nlogn)

虚树

代码

  • 树链剖分+线段树1
// %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=2e5+5; int t,n,q,fa[N],son[N],dep[N],id[N],sz[N],cnt,top[N]; vector<int> adj[N]; struct Tr { int l,r,v1,v2,tag,clr; }tr[N<<2]; void dfs(int x,int father,int depth) { sz[x]=1,fa[x]=father,dep[x]=depth; for(int y:adj[x]) { if(y==father)continue; dfs(y,x,depth+1); sz[x]+=sz[y]; if(sz[son[x]]<sz[y])son[x]=y; } } void dfs1(int x,int t) { id[x]=++cnt,top[x]=t; if(!son[x])return ; dfs1(son[x],t); for(int y:adj[x]) { if(y==fa[x]||y==son[x])continue; dfs1(y,y); } } void pushup(int u) { tr[u].v1=tr[u<<1].v1&tr[u<<1|1].v1; tr[u].v2=tr[u<<1].v2|tr[u<<1|1].v2; } void pushdown(int u) { if(tr[u].clr) { tr[u<<1].clr=tr[u<<1|1].clr=1; tr[u<<1].v1=tr[u<<1].v2=tr[u<<1].tag=0; tr[u<<1|1].v1=tr[u<<1|1].v2=tr[u<<1|1].tag=0; tr[u].clr=0; } if(tr[u].tag) { tr[u<<1].v1|=tr[u].tag,tr[u<<1].v2|=tr[u].tag,tr[u<<1].tag|=tr[u].tag; tr[u<<1|1].v1|=tr[u].tag,tr[u<<1|1].v2|=tr[u].tag,tr[u<<1|1].tag|=tr[u].tag; tr[u].tag=0; } } void build(int u,int l,int r) { tr[u]={l,r,0,0,0,0}; if(l==r)return ; int mid=l+r>>1; build(u<<1,l,mid),build(u<<1|1,mid+1,r); } void update(int u,int l,int r,int k) { if(l<=tr[u].l&&tr[u].r<=r) { tr[u].v1|=k; tr[u].v2|=k; tr[u].tag|=k; return ; } pushdown(u); int mid=tr[u].l+tr[u].r>>1; if(l<=mid)update(u<<1,l,r,k); if(r>mid)update(u<<1|1,l,r,k); pushup(u); } void update_path(int u,int v,int k) { while(top[u]!=top[v]) { if(dep[top[u]]<dep[top[v]])swap(u,v); update(1,id[top[u]],id[u],k); u=fa[top[u]]; } if(dep[u]>dep[v])swap(u,v); update(1,id[u],id[v],k); } void update_tree(int u,int k) { update(1,id[u],id[u]+sz[u]-1,k); } int ask(int u,int l,int r) { if(l<=tr[u].l&&tr[u].r<=r) { if(tr[u].v1>=7)return tr[u].r-tr[u].l+1; if(tr[u].l==tr[u].r)return 0; } pushdown(u); int res=0; int mid=tr[u].l+tr[u].r>>1; if(l<=mid&&tr[u<<1].v2>=7)res+=ask(u<<1,l,r); if(r>mid&&tr[u<<1|1].v2>=7)res+=ask(u<<1|1,l,r); return res; } int main() { help; for(cin>>t;t;t--) { cin>>n>>q; for(int i=1;i<=n;i++)adj[i].clear(); for(int i=1;i<n;i++) { int r; cin>>r; adj[i+1].pb(r),adj[r].pb(i+1); } cnt=0; dfs(1,0,1); dfs1(1,1); build(1,1,n); while(q--) { int a,b,c,x; cin>>a>>b>>c; for(int i=1;i<=a;i++) { cin>>x; update_path(1,x,1); } for(int i=1;i<=b;i++) { cin>>x; update_path(1,x,2); } for(int i=1;i<=c;i++) { cin>>x; update_tree(x,4); } cout<<ask(1,1,n)<<'\n'; tr[1].v1=tr[1].v2=tr[1].tag=0,tr[1].clr=1; } } return 0; }
  • 树链剖分+线段树2
// %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=2e5+5; int t,n,q,fa[N],son[N],dep[N],id[N],sz[N],cnt,top[N]; int source[3][4]={{0,2,4,6},{0,1,4,5},{0,1,2,3}}; int target[3][4]={{1,3,5,7},{2,3,6,7},{4,5,6,7}}; vector<int> adj[N]; struct Tr { int l,r,res[8],tag,clr; }tr[N<<2]; void dfs(int x,int father,int depth) { sz[x]=1,fa[x]=father,dep[x]=depth; for(int y:adj[x]) { if(y==father)continue; dfs(y,x,depth+1); sz[x]+=sz[y]; if(sz[son[x]]<sz[y])son[x]=y; } } void dfs1(int x,int t) { id[x]=++cnt,top[x]=t; if(!son[x])return ; dfs1(son[x],t); for(int y:adj[x]) { if(y==fa[x]||y==son[x])continue; dfs1(y,y); } } void pushup(int u) { for(int i=0;i<8;i++)tr[u].res[i]=tr[u<<1].res[i]+tr[u<<1|1].res[i]; } void pushdown(int u) { if(tr[u].clr) { tr[u<<1].clr=tr[u<<1|1].clr=1; for(int i=0;i<8;i++) tr[u<<1].res[i]=tr[u<<1|1].res[i]=0; tr[u<<1].tag=tr[u<<1|1].tag=0; tr[u<<1].res[0]=tr[u<<1].r-tr[u<<1].l+1; tr[u<<1|1].res[0]=tr[u<<1|1].r-tr[u<<1|1].l+1; tr[u].clr=0; } if(tr[u].tag) { for(int i=0;i<3;i++) if(tr[u].tag>>i&1) for(int j=0;j<4;j++) tr[u<<1].res[target[i][j]]+=tr[u<<1].res[source[i][j]], tr[u<<1|1].res[target[i][j]]+=tr[u<<1|1].res[source[i][j]], tr[u<<1].res[source[i][j]]=tr[u<<1|1].res[source[i][j]]=0; tr[u<<1].tag|=tr[u].tag; tr[u<<1|1].tag|=tr[u].tag; tr[u].tag=0; } } void build(int u,int l,int r) { tr[u]={l,r,{1,0,0,0,0,0,0,0},0,0}; if(l==r)return ; int mid=l+r>>1; build(u<<1,l,mid),build(u<<1|1,mid+1,r); pushup(u); } void update(int u,int l,int r,int k) { if(l<=tr[u].l&&tr[u].r<=r) { for(int i=0;i<4;i++) tr[u].res[target[k][i]]+=tr[u].res[source[k][i]], tr[u].res[source[k][i]]=0; tr[u].tag|=(1<<k); return ; } pushdown(u); int mid=tr[u].l+tr[u].r>>1; if(l<=mid)update(u<<1,l,r,k); if(r>mid)update(u<<1|1,l,r,k); pushup(u); } void update_path(int u,int v,int k) { while(top[u]!=top[v]) { if(dep[top[u]]<dep[top[v]])swap(u,v); update(1,id[top[u]],id[u],k); u=fa[top[u]]; } if(dep[u]>dep[v])swap(u,v); update(1,id[u],id[v],k); } void update_tree(int u,int k) { update(1,id[u],id[u]+sz[u]-1,k); } int main() { for(read(t);t;t--) { read(n),read(q); for(int i=1;i<=n;i++)adj[i].clear(); for(int i=1;i<n;i++) { int r; read(r); adj[i+1].pb(r),adj[r].pb(i+1); } cnt=0; dfs(1,0,1); dfs1(1,1); build(1,1,n); while(q--) { int a,b,c,x; read(a),read(b),read(c); for(int i=1;i<=a;i++) { read(x); update_path(1,x,0); } for(int i=1;i<=b;i++) { read(x); update_path(1,x,1); } for(int i=1;i<=c;i++) { read(x); update_tree(x,2); } printf("%d\n",tr[1].res[7]); for(int i=1;i<8;i++)tr[1].res[i]=0; tr[1].res[0]=tr[1].r-tr[1].l+1; tr[1].tag=0; tr[1].clr=1; } } return 0; }
  • 树链剖分+区间问题
// %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=2e5+5; int t,n,q,fa[N],son[N],dep[N],id[N],sz[N],cnt,top[N]; vector<int> adj[N]; void dfs(int x,int father,int depth) { sz[x]=1,fa[x]=father,dep[x]=depth; for(int y:adj[x]) { if(y==father)continue; dfs(y,x,depth+1); sz[x]+=sz[y]; if(sz[son[x]]<sz[y])son[x]=y; } } void dfs1(int x,int t) { id[x]=++cnt,top[x]=t; if(!son[x])return ; dfs1(son[x],t); for(int y:adj[x]) { if(y==fa[x]||y==son[x])continue; dfs1(y,y); } } void update_path(int u,int v,vector<PII> &a) { while(top[u]!=top[v]) { if(dep[top[u]]<dep[top[v]])swap(u,v); a.pb({id[top[u]],id[u]}); u=fa[top[u]]; } if(dep[u]>dep[v])swap(u,v); a.pb({id[u],id[v]}); } void update_tree(int u,vector<PII> &a) { a.pb({id[u],id[u]+sz[u]-1}); } void merge(vector<PII> &a) { vector<PII> res; sort(a.begin(),a.end()); int l=a[0].fi,r=a[0].se;; for(int i=1;i<a.size();i++) if(a[i].fi<=r)r=max(r,a[i].se); else res.pb({l,r}),l=a[i].fi,r=a[i].se; res.pb({l,r}); a=res; } int main() { help; for(cin>>t;t;t--) { cin>>n>>q; for(int i=1;i<=n;i++)adj[i].clear(); for(int i=1;i<n;i++) { int r; cin>>r; adj[i+1].pb(r),adj[r].pb(i+1); } cnt=0; dfs(1,0,1); dfs1(1,1); while(q--) { int a,b,c,x; cin>>a>>b>>c; vector<PII> A,B,C; for(int i=1;i<=a;i++) { cin>>x; update_path(1,x,A); } for(int i=1;i<=b;i++) { cin>>x; update_path(1,x,B); } for(int i=1;i<=c;i++) { cin>>x; update_tree(x,C); } merge(A),merge(B),merge(C); a=b=c=0; int res=0,l=0,r; while(a<A.size()&&b<B.size()&&c<C.size()) { while(a<A.size()&&A[a].se<=l)a++; while(b<B.size()&&B[b].se<=l)b++; while(c<C.size()&&C[c].se<=l)c++; if(!(a<A.size()&&b<B.size()&&c<C.size()))break; l=max({A[a].fi,B[b].fi,C[c].fi}); r=min({A[a].se,B[b].se,C[c].se}); res+=max(0,r-l+1); l=r; } cout<<res<<'\n'; } } return 0; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/16516831.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(113)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示

喜欢请打赏

扫描二维码打赏

支付宝打赏