一句话题意:求一个无向联通图的桥的个数,动态加边
一开始的想法是先缩点然后树剖或者并查集缩一发,感觉十分的,不好敲
然后被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
这样子暴力