C137 线段树分治+并查集 P2147 [SDOI2008] 洞穴勘测
视频链接:C137 线段树分治 P2147 [SDOI2008] 洞穴勘测_哔哩哔哩_bilibili
P2147 [SDOI2008] 洞穴勘测 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
// 线段树分治 O(mlogmlogm) #include <iostream> #include <cstring> #include <algorithm> #include <vector> #include <map> using namespace std; #define ls (u<<1) #define rs (u<<1|1) #define mid ((l+r)>>1) const int N=200005; int n,m,top,ans[N]; int p[N],siz[N]; //并查集 struct node{ int x,y; }st[N]; //栈 struct Q{ int x,y,b; //b=1是查询,b=0不是 bool operator<(const Q &a)const{ return x==a.x?y<a.y:x<a.x; } }q[N]; //操作 vector<Q>tr[N<<2]; //节点 map<Q,int> mp; //操作信息<<x,y,b>,i> void insert(int u,int l,int r,int L,int R,Q q){ if(l>R||r<L) return; if(L<=l&&r<=R) return tr[u].push_back(q); insert(ls,l,mid,L,R,q); insert(rs,mid+1,r,L,R,q); } int find(int x){ //查找根 return p[x]==x?x:find(p[x]); } void merge(int x,int y){ //合并集合 x=find(x),y=find(y); if(x==y) return; if(siz[x]>siz[y]) swap(x,y); st[++top]={x,y}; p[x]=y; siz[y]+=siz[x]; } void solve(int u,int l,int r){ int now=top; for(auto i:tr[u]) merge(i.x,i.y); if(l==r){ if(q[l].b) ans[l]=find(q[l].x)==find(q[l].y); } else solve(ls,l,mid),solve(rs,mid+1,r); while(top>now){ //撤销合并 node t=st[top--]; p[t.x]=t.x; siz[t.y]-=siz[t.x]; } } int main(){ scanf("%d%d",&n,&m); char op[9]; for(int i=1;i<=n;i++) p[i]=i,siz[i]=1; for(int i=1,x,y;i<=m;i++){ scanf("%s%d%d",op,&x,&y); if(x>y) swap(x,y); //保证映射唯一 if(op[0]=='C'){ q[i]={x,y,0}; mp[q[i]]=i; //记录边q[i]的出现时刻 } else if(op[0]=='D'){ q[i]={x,y,0}; insert(1,1,m,mp[q[i]],i,q[i]);//插入q[i]的出现时间 mp.erase(q[i]); //删除边q[i] } else q[i]={x,y,1}; //q[i]是查询 } for(auto i:mp) //插入每条边的出现时间 insert(1,1,m,i.second,m,i.first); solve(1,1,m); for(int i=1;i<=m;i++)if(q[i].b)puts(ans[i]?"Yes":"No"); }
// 线段树分治 O(mlogmlogm) #include <iostream> #include <cstring> #include <algorithm> #include <vector> #include <map> using namespace std; #define ls (u<<1) #define rs (u<<1|1) #define mid ((l+r)>>1) #define pii pair<int,int> const int N=200005; int n,m,top; int p[N],siz[N]; //并查集 struct node{ int x,y; }e[N],st[N]; //栈 bool qb[N]; //是否查询 vector<pii>tr[N<<2]; //节点 map<pii,int> mp; //操作信息<<x,y>,i> void insert(int u,int l,int r,int L,int R,pii e){ if(l>R||r<L) return; if(L<=l&&r<=R) return tr[u].push_back(e); insert(ls,l,mid,L,R,e); insert(rs,mid+1,r,L,R,e); } int find(int x){ //查找根 return p[x]==x?x:find(p[x]); } void merge(int x,int y){ //合并集合 x=find(x),y=find(y); if(x==y) return; if(siz[x]>siz[y]) swap(x,y); st[++top]={x,y}; p[x]=y; siz[y]+=siz[x]; } void solve(int u,int l,int r){ int now=top; for(auto i:tr[u]) merge(i.first,i.second); if(l==r){ if(qb[l]) puts(find(e[l].x)==find(e[l].y)?"Yes":"No"); } else solve(ls,l,mid),solve(rs,mid+1,r); while(top>now){ //撤销合并 node t=st[top--]; p[t.x]=t.x; siz[t.y]-=siz[t.x]; } } int main(){ scanf("%d%d",&n,&m); char op[9]; for(int i=1;i<=n;i++) p[i]=i,siz[i]=1; for(int i=1,x,y;i<=m;i++){ scanf("%s%d%d",op,&x,&y); if(x>y) swap(x,y); //保证映射唯一 pii t={x,y}; e[i]={x,y}; if(op[0]=='C')mp[t]=i; //记录边的出现时刻 else if(op[0]=='D'){ insert(1,1,m,mp[t],i,t); //插入边的出现时间 mp.erase(t); //删除边 } else qb[i]=1; //i是查询 } for(auto i:mp) //插入每条边的出现时间 insert(1,1,m,i.second,m,i.first); solve(1,1,m); }
分类:
C 数据结构
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!