YbtOJ-交换游戏【树链剖分,线段树合并】

1|0正题


1|1题目大意

给出两棵树,对于第一棵树的每一条边(x,y)询问有多少条在第二棵树上的边(u,v)与其交换(连接的序号相同)后两棵树依旧是一棵树。

1n2×105


1|2解题思路

先只考虑一棵树的合法情况,对于第二棵树的边(u,v)交换过来合法的当且仅当(x,y)uv路径上,同理的对于第二棵树合法当且仅当(u,v)xy路径上。

那么考虑限制一个条件,第二个条件用数据结构查询。

我们把所有的(u,v)用树上差分挂在第一棵树的uv路径上,然后遇到一条(u,v)我们让这棵树的这条边权值+1

这样我们就保证了处理到边(x,y)时有权值的只有在第一棵树上经过(x,y)uv,那么至于第二个要求我们直接在第二棵树上查询xy路径上的权值和。这个可以用树链剖分维护。

而树上差分的合并功能就用线段树合并就好了。

时间复杂度:O(nlog2n)


1|3code

#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #define mp(x,y) make_pair(x,y) using namespace std; const int N=2e5+10,U=20; int n,cnt,depG[N],f[N][U],rt[N],ans[N]; int siz[N],fa[N],dep[N],son[N],top[N],id[N]; vector<pair<int,int> > G[N]; vector<int> T[N],v[N]; pair<int,int> e[N]; struct SegTree{ int w[N<<5],ls[N<<5],rs[N<<5]; void Change(int &x,int L,int R,int pos,int val){ if(!x)x=++cnt;w[x]+=val; if(L==R)return;int mid=(L+R)>>1; if(pos<=mid)Change(ls[x],L,mid,pos,val); else Change(rs[x],mid+1,R,pos,val); } int Ask(int x,int L,int R,int l,int r){ if(!x)return 0; if(L==l&&R==r)return w[x]; int mid=(L+R)>>1; if(r<=mid)return Ask(ls[x],L,mid,l,r); if(l>mid)return Ask(rs[x],mid+1,R,l,r); return Ask(ls[x],L,mid,l,mid)+Ask(rs[x],mid+1,R,mid+1,r); } int Merge(int x,int y,int L,int R){ if(!x||!y)return x|y;w[x]+=w[y]; if(L==R)return x; int mid=(L+R)>>1; ls[x]=Merge(ls[x],ls[y],L,mid); rs[x]=Merge(rs[x],rs[y],mid+1,R); return x; } }S; void preset(int x,int fa){ f[x][0]=fa;depG[x]=depG[fa]+1; for(int i=0;i<G[x].size();i++){ int y=G[x][i].first; if(y==fa)continue; preset(y,x); } return; } int LCA(int x,int y){ if(depG[x]<depG[y])swap(x,y); for(int i=U-1;i>=0;i--) if(depG[f[x][i]]>=depG[y])x=f[x][i]; if(x==y)return x; for(int i=U-1;i>=0;i--) if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; return f[x][0]; } void dfs1(int x){ dep[x]=dep[fa[x]]+1;siz[x]=1; for(int i=0;i<T[x].size();i++){ int y=T[x][i]; if(y==fa[x])continue; fa[y]=x;dfs1(y); siz[x]+=siz[y]; if(siz[y]>siz[son[x]]) son[x]=y; } return; } void dfs2(int x){ id[x]=++cnt; if(son[x]){ top[son[x]]=top[x]; dfs2(son[x]); } for(int i=0;i<T[x].size();i++){ int y=T[x][i]; if(y==fa[x]||y==son[x])continue; top[y]=y;dfs2(y); } return; } int GetAns(int x,int y,int rt){ int ans=0; while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); ans+=S.Ask(rt,1,n,id[top[x]],id[x]); x=fa[top[x]]; } if(dep[x]>dep[y])swap(x,y); if(x!=y)ans+=S.Ask(rt,1,n,id[x]+1,id[y]); return ans; } void solve(int x,int fa,int ids){ for(int i=0;i<G[x].size();i++){ int y=G[x][i].first,id=G[x][i].second; if(y==fa)continue; solve(y,x,id); rt[x]=S.Merge(rt[x],rt[y],1,n); } if(ids){ for(int i=0;i<v[x].size();i++){ int p=abs(v[x][i]); int X=e[p].first,Y=e[p].second; if(dep[X]>dep[Y])swap(X,Y); S.Change(rt[x],1,n,id[Y],(v[x][i]>0)?1:-2); } ans[ids]=GetAns(fa,x,rt[x]); } return; } int main() { freopen("exchange.in","r",stdin); freopen("exchange.out","w",stdout); scanf("%d",&n); for(int i=1;i<n;i++){ int x,y; scanf("%d%d",&x,&y); G[x].push_back(mp(y,i)); G[y].push_back(mp(x,i)); } for(int i=1;i<n;i++){ int x,y; scanf("%d%d",&x,&y); T[x].push_back(y); T[y].push_back(x); e[i]=mp(x,y); } preset(1,0); for(int j=1;j<U;j++) for(int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1]; for(int i=1;i<n;i++){ int x=e[i].first,y=e[i].second,lca=LCA(x,y); v[x].push_back(i);v[y].push_back(i); v[lca].push_back(-i); } dfs1(1);top[1]=1;dfs2(1); solve(1,0,0); for(int i=1;i<n;i++) printf("%d ",ans[i]); return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/15896829.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(96)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
历史上的今天:
2021-02-15 P5319-[BJOI2019]奥术神杖【0/1分数规划,AC自动机,dp】
2021-02-15 P4585-[FJOI2015]火星商店问题【线段树,可持久化Trie】
2021-02-15 P6793-[SNOI2020]字符串【广义SAM,贪心】
2021-02-15 CF803G-Periodic RMQ Problem【离散化,线段树,ST表】
2021-02-15 YbtOJ#593-木棍问题【费用流】
2021-02-15 YbtOJ#893-带权的图【高斯消元,结论】
2021-02-15 YbtOJ#631-次短路径【左偏树,最短路】
点击右上角即可分享
微信分享提示