Poj 3694 Network

一句话题意:求一个无向联通图的桥的个数,动态加边


一开始的想法是先缩点然后树剖或者并查集缩一发,感觉十分的,不好敲


然后被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
这样子暴力

你可能感兴趣的:(Poj 3694 Network)