一句话题意:求一个无向联通图的桥的个数,动态加边
一开始的想法是先缩点然后树剖或者并查集缩一发,感觉十分的,不好敲
然后被csy点悟,学会了一种神™好敲的方法:
先找出一个生成树,对树上的每个边的权值都赋为1
然后对于非树边,更新这个非树边的两个端点在生成树上的路径所有边的权值为0
那么最后所有的边权的和就是割边的个数
对于动态加的边,按照非树边处理即可
那么就是 并查集+树剖+线段树 了
比缩点不知道高到哪里去了
几乎是一个树剖的裸题了?
顺便学会了树剖对边权的处理
把边权下推到离根远的节点,那么除了根节点之外,其它的边和节点是一一对应的
然后在更新/查询的时候,是更新树上的除了LCA之外的两点之间的路径。
具体到代码上来,就是在最后一步的时候抠掉LCA
(再具体一点就看代码吧
以上
---------------------------------------------久违的代码昏割线---------------------------
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 112345;
class uf{
int arr[maxn];
public:
void init(int n){
for(int i=0;i<=n;i++){
arr[i] = i;
}
}
int fnd(int x){
return arr[x] == x ? x : arr[x] = fnd(arr[x]);
}
bool join(int x,int y){
x = fnd(x);
y = fnd(y);
if(x == y)
return false;
arr[x] = y;
return true;
}
};
vector<int> edge[maxn];
void init(int n){
for(int i=0;i<=n;i++){
edge[i].resize(0);
}
}
int nou[maxn];
int nov[maxn];
// seg tree
#define root 1,1,n
#define Now int o,int l,int r
#define lson o<<1 ,l ,m
#define rson o<<1|1,m+1,r
#define Mid int m = l+(r-l)/2
int val[maxn*4];
int lazy[maxn*4];
int query(Now){
//query sum in ud ?
return val[o];
}
void down(Now){
if(l==r)
return;
int v = lazy[o];
lazy[o] = -1;
Mid;
val[o<<1] = v * (m - l + 1);
val[o<<1|1] = v * (r - (m + 1) + 1);
lazy[o<<1] = lazy[o<<1|1] = v;
}
void update(Now,int ul,int ur,int v){
if(ul <= l && r <= ur){
lazy[o] = v;
val[o] = v * (r - l + 1);
return;
}
Mid;
if(lazy[o] != -1){
down(o,l,r);
}
if(ul <= m){
update(lson,ul,ur,v);
}
if(m+1 <= ur){
update(rson,ul,ur,v);
}
val[o] = val[o<<1] + val[o<<1|1];
}
// treesplite
int fa[maxn],top[maxn],son[maxn];
int siz[maxn],tid[maxn],deep[maxn],_cnt;
void dffs(int st,int Fa,int Deep){
deep[st] = Deep,son[st]=-1,siz[st]=1,fa[st]=Fa;
for(vector<int>::iterator it = edge[st].begin();it!=edge[st].end();it++){
int x = *it;
if(x!=Fa){
dffs(x,st,Deep+1);
siz[st] += siz[x];
if(son[st] == -1 || siz[son[st]] < siz[x])
son[st] = x;
}
}
}
void dfss(int st,int Top){
tid[st] = _cnt++;
top[st] = Top;
if(son[st] != -1)
dfss(son[st],Top);
for(vector<int>::iterator it = edge[st].begin();it!=edge[st].end();it++){
int x = *it;
if(x!=fa[st] && x!= son[st]){
dfss(x,x);
}
}
}
void lnk(int x,int y,int n){
int tx = top[x];
int ty = top[y];
while(tx != ty){
if(deep[tx] > deep[ty]){
update(root,tid[tx],tid[x],0);
x = fa[tx],tx = top[x];
}
else{
update(root,tid[ty],tid[y],0);
y = fa[ty],ty = top[y];
}
}
if(deep[x] < deep[y]){
update(root,tid[x]+1,tid[y],0);
}
if(deep[x] > deep[y]){
update(root,tid[y]+1,tid[x],0);
}
}
void Link(int st,int ed){
edge[st].push_back(ed);
edge[ed].push_back(st);
}
int main(){
int n,m;
uf UF;
int icase = 1;
while(~scanf("%d %d",&n,&m) && (n||m)){
UF.init(n);
init(n);
int u,v;
int len = 0;
while(m--){
scanf("%d %d",&u,&v);
if(UF.join(u,v))
Link(u,v);
else{
nou[len] = u;
nov[len] = v;
len++;
}
}
// tree splite
_cnt = 1;
dffs(1,0,1);
dfss(1,1);
// set all edge value
memset(lazy,-1,sizeof(lazy));
memset(val,0,sizeof(val));
update(root,1,n,1);
update(root,1,1,0);
for(int i=0;i<len;i++){
lnk(nou[i],nov[i],n);
}
scanf("%d",&m);
printf("Case %d:\n",icase++);
while(m--){
scanf("%d %d",&u,&v);
lnk(u,v,n);
printf("%d\n",query(root));
}
puts("");
}
return 0;
}
------------------------------------------然而---------------------------------------------
这个线段树部分其实是不需要的,只要用并查集暴力即可
(然而窝并没有看懂
谨在这里留下csy的讲题记录 日后查阅
pro: 区间赋为0,查询整个区间的和
其实你可以暴力呀
注意到每个点只会被赋值一次
于是冰茶几就好了
一开始不是都是1么
然后你每次是区间赋0
那我就设
f[x]表示x右边第一个1的位置
这个可以并查集
然后每次i=findfather(i)+1
这样子暴力