C132 线段树分治+并查集 CF1814F Communication Towers

视频链接:C132 线段树分治 CF1814F Communication Towers_哔哩哔哩_bilibili

 

 

 

Communication Towers - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

Problem - 1814F - Codeforces

复制代码
// 线段树分治 O(mlognlogn)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

#define int long long
#define ls (u<<1)
#define rs (u<<1|1)
#define mid ((l+r)>>1)
#define N 200005
int n,m,a[N],b[N];
int p[N],high[N],tag[N],top;
vector<pair<int,int> >tr[N<<2]; //记录时间区间上出现的边
struct node{
  int x,y,hy;
}st[N<<1]; //用栈记录边的端点的合并信息

void insert(int u,int l,int r,int L,int R,int x,int y){
  if(L>r||R<l) return;
  if(L<=l&&r<=R){
    tr[u].push_back({x,y}); //当前节点(时间区间)出现的边
    return;
  }
  insert(ls,l,mid,L,R,x,y);
  insert(rs,mid+1,r,L,R,x,y);
}
int find(int x){ //查找根
  if(p[x]==x) return x;
  return find(p[x]);
}
void merge(int x,int y){ //合并集合
  x=find(x),y=find(y);
  if(x==y) return;
  if(high[x]>high[y]) swap(x,y);
  st[++top]={x,y,high[y]}; //记录合并信息
  p[x]=y; //x指向y
  high[y]+=(high[x]==high[y]);
  tag[x]-=tag[y]; //消除y上已有标记的影响
}
void solve(int u,int l,int r){
  int now=top;
  for(int i=0;i<tr[u].size();i++)
    merge(tr[u][i].first,tr[u][i].second);
    
  if(l==r)tag[find(1)]++; //r时刻根上打标记
  else solve(ls,l,mid),solve(rs,mid+1,r);
  
  while(top>now){ //撤销合并
    node s=st[top--];
    p[s.x]=s.x;
    high[s.y]=s.hy;
    tag[s.x]+=tag[s.y]; //下传标记
  }
}
signed main(){
  cin>>n>>m; int k=2e5;
  for(int i=1;i<=n;i++)p[i]=i;
  for(int i=1;i<=n;i++)scanf("%lld%lld",&a[i],&b[i]);
  for(int i=1;i<=m;i++){
    int x,y;scanf("%lld%lld",&x,&y);
    int l=max(a[x],a[y]),r=min(b[x],b[y]);
    if(l<=r) insert(1,1,k,l,r,x,y); //出现时间有交集则插入
  }
  solve(1,1,k); //线段树分治
  for(int i=1;i<=n;i++) if(tag[i])printf("%lld ",i);
  return 0;
}
复制代码

 

复制代码
// 线段树分治 O(mlognlogn)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

#define int long long
#define ls (u<<1)
#define rs (u<<1|1)
#define mid ((l+r)>>1)
#define N 200005
int n,m,a[N],b[N];
int p[N],siz[N],tag[N],top;
vector<pair<int,int> >tr[N<<2]; //记录时间区间上出现的边
struct node{
  int x,y;
}st[N<<1]; //用栈记录边的端点的合并信息

void insert(int u,int l,int r,int L,int R,int x,int y){
  if(L>r||R<l) return;
  if(L<=l&&r<=R){
    tr[u].push_back({x,y}); //当前节点(时间区间)出现的边
    return;
  }
  insert(ls,l,mid,L,R,x,y);
  insert(rs,mid+1,r,L,R,x,y);
}
int find(int x){ //查找根
  if(p[x]==x) return x;
  return 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; //x指向y
  siz[y]+=siz[x];
  tag[x]-=tag[y]; //消除y上已有标记的影响
}
void solve(int u,int l,int r){
  int now=top;
  for(int i=0;i<tr[u].size();i++)
    merge(tr[u][i].first,tr[u][i].second);
    
  if(l==r)tag[find(1)]++; //r时刻根上打标记
  else solve(ls,l,mid),solve(rs,mid+1,r);
  
  while(top>now){ //撤销合并
    node s=st[top--];
    p[s.x]=s.x;
    siz[s.y]-=siz[s.x];
    tag[s.x]+=tag[s.y]; //下传标记
  }
}
signed main(){
  cin>>n>>m; int k=2e5;
  for(int i=1;i<=n;i++)p[i]=i,siz[i]=1;
  for(int i=1;i<=n;i++)scanf("%lld%lld",&a[i],&b[i]);
  for(int i=1;i<=m;i++){
    int x,y;scanf("%lld%lld",&x,&y);
    int l=max(a[x],a[y]),r=min(b[x],b[y]);
    if(l<=r) insert(1,1,k,l,r,x,y); //出现时间有交集则插入
  }
  solve(1,1,k); //线段树分治
  for(int i=1;i<=n;i++) if(tag[i])printf("%lld ",i);
  return 0;
}
复制代码

 

posted @   董晓  阅读(211)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示