Explorer(线段树时间分治+并查集)

题目
题意: 有n个点 m条边 每条边u v w1 w2.要想从这个边经过必须sz在[w1,w2]之间。问sz的合法(1 n联通)个数是多少?
思路: 将题目转化为这个边在时间[w1,w2]处存在 问多少时刻1-n联通。 建立一颗时间线段树。对线段树中的每个节点存一个node[]数组 装在[l,r]的时刻需要添加的[u,v]边。
然后dfs这个线段树 这个节点node不空的时候就并查集连关系 每到叶子节点判断一下1-n是否连通并累计ans 回溯的时候删除以前并查集建立的影响 用stack保存一下 要按秩合并 不能路径压缩。
其次就是注意emmm我还是有点。。因为一些问题。。refl[++tot]=e[i].w2+1;(假点)
然后每次插入到线段树的离散操作。。然后每次累计ans的操作。

int dex1=lower_bound(refl+1,refl+tot+1,e[i].w1)-refl;
int dex2=lower_bound(refl+1,refl+tot+1,e[i].w2+1)-refl-1;
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
typedef pair<int,int>pii;
vector<pii>node[N<<3];
stack<pii>s;
struct Edge{int u,v,w1,w2;}e[N];
int refl[N<<2],tot;
int f[N],sz[N],n;
void chanspan(int cl,int cr,int l,int r,int pos,pii t)
{
    if(cl<=l&&r<=cr){
        node[pos].push_back(t);
        return;
    }
    int mid=(l+r)>>1;
    if(cl<=mid) chanspan(cl,cr,l,mid,pos<<1,t);
    if(cr>mid) chanspan(cl,cr,mid+1,r,pos<<1|1,t);
}
inline int seek(int x){
    while(f[x]!=x) x=f[x];
    return x;
}
inline void combine(pii t){
    int x=seek(t.first),y=seek(t.second);
    if(x==y) return;
    if(sz[x]>sz[y]) swap(x,y);
    f[x]=y,sz[y]+=sz[x];
    s.push(make_pair(x,y));
}
inline void _back(pii t){
    int x=t.first,y=t.second;
    f[x]=x,sz[y]-=sz[x];
}
int ans=0;
void dfs(int l,int r,int pos)
{
    int prenum=s.size();
    for(pii i:node[pos]) combine(i);
    if(l==r){
        if(seek(1)==seek(n)) ans+=refl[l+1]-refl[l];
    }
    else{
        int mid=(l+r)>>1;
        dfs(l,mid,pos<<1),dfs(mid+1,r,pos<<1|1);
    }
    while(s.size()!=prenum) _back(s.top()),s.pop();
}
int main()
{
    int m;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i) f[i]=i,sz[i]=1;
    for(int i=1;i<=m;++i){
        scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].w1,&e[i].w2);
        refl[++tot]=e[i].w1,refl[++tot]=e[i].w2+1;
    }
    sort(refl+1,refl+tot+1);
    tot=unique(refl+1,refl+tot+1)-(refl+1);
    for(int i=1;i<=m;++i)
    {
        int dex1=lower_bound(refl+1,refl+tot+1,e[i].w1)-refl;
        int dex2=lower_bound(refl+1,refl+tot+1,e[i].w2+1)-refl-1;
        chanspan(dex1,dex2,1,tot,1,make_pair(e[i].u,e[i].v));
    }
    dfs(1,tot,1);
    printf("%d\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值