题目
题意: 有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);
}