【BZOJ 4568】[Scoi2016] 幸运数字

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4568
数据生成器:http://paste.ubuntu.com/23202923/

《浅谈省选的时候还不会线性基的悲伤有多大》
今天重新看了一看,结果最开始写的是链剖,T到死 ←是男人就去ZGS的OJ上测,单点时限
后来重新写了LCA的版本,又T到死
于是暴力优化常数,最终5.9s勉强卡过

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 20000+9; 

int n,q,head[N],nxt[N*2],to[N*2];
LL arr[N];

inline int read(){char c=getchar(); int ret=0;while (c<'0'||c>'9') c=getchar(); while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}return ret;}
inline LL readL(){char c=getchar(); LL ret=0; while (c<'0'||c>'9') c=getchar(); while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}return ret;}

inline void Add_Edge(int u, int v) {
	static int T = 0;
	to[++T] = v; nxt[T] = head[u]; head[u] = T;
	to[++T] = u; nxt[T] = head[v]; head[v] = T;
}

namespace LCA{
	int fa[N][16],dep[N];
	LL f[N][16][61],POW[61];

	void DFS(int w, int f) {
		fa[w][1] = f; fa[w][0] = w; dep[w] = dep[f] + 1; 
		for (int i=head[w];i;i=nxt[i]) if (to[i] != f) DFS(to[i],w);
	} inline void DFS(){DFS(1,1);for (int i=0;i<=60;i++) POW[i] = 1LL<<i;}
	
	inline void merge(LL *a, LL *b) {
		for (int i=60;~i;i--) if (b[i]) {
			LL w = b[i]; for (int j=60;~j;j--) if (w&POW[j]) {
				if (!a[j]) {a[j] = w; break;}
				else w ^= a[j];
			}
		} 
	}
	
	inline void init() {
		for (int j=1;j<=n;j++) for (int i=60;~i;i--) if (POW[i]&arr[j]) {f[j][0][i] = arr[j]; break;}
		for (int i=1;i<=n;i++) memcpy(f[i][1],f[i][0],sizeof(f[i][0])), merge(f[i][1],f[fa[i][1]][0]);
		for (int i=1;i<=n;i++) for (int j=2;j<=15;j++) 
			fa[i][j] = fa[fa[i][j-1]][j-1], 
			memcpy(f[i][j],f[i][j-1],sizeof(f[i][j])),
			merge(f[i][j],f[fa[i][j-1]][j-1]);
	}
	
	inline LL query(int x, int y) {
		static LL bas[61]; memset(bas,0,sizeof(bas));
		if (dep[x] < dep[y]) swap(x,y);
		for (int i=15;~i;i--) if (dep[fa[x][i]] > dep[y]) 
			merge(bas,f[x][i]), x = fa[fa[x][i]][1];
		if (x == y) merge(bas,f[x][0]);  
		else {
			for (int i=15;~i;i--)  if (fa[x][i] != fa[y][i])
				merge(bas,f[x][i]), merge(bas,f[y][i]), 
				x = fa[fa[x][i]][1], y = fa[fa[y][i]][1];
			merge(bas,f[x][0]); 
		} LL ret = 0;
		for (int i=0;i<=60;i++) if (bas[i]) 
		for (int j=i+1;j<=60;j++) if (bas[j]&POW[i]) bas[j] ^= bas[i];
		for (int i=0;i<=60;i++) ret ^= bas[i];
		return ret;
	}	
};

int main(){
	n = read(); q = read();
	for (int i=1;i<=n;i++) arr[i] = readL();
	for (int i=1;i<n;i++) Add_Edge(read(),read());
	LCA::DFS(); LCA::init(); 
	for (int i=1;i<=q;i++) printf("%lld\n",LCA::query(read(),read()));
	return 0;
}

另外,%Claris,直接上点分治:http://www.cnblogs.com/clrs97/p/5451814.html

【算法笔记】仙人掌

仙人掌这个东西,肯定是前排膜拜VFK:http://vfleaking.blog.uoj.ac/slide/89#/
然后嘛,就是狂膜一波CA爷:http://blog.csdn.net/creationaugust/article/details/48007069
仍然膜拜CA爷:http://blog.csdn.net/creationaugust/article/details/48022291

反正我的仙人掌栽培技术就这水平了
什么世界领先的动态仙人掌栽培技术,还是算了吧QAQ
ps:感觉CA的东西比VFK的实用?
psx2:感觉wc上这张图还是很带感的,贴上来 = ̄ω ̄=
21467125346712

【POJ 3567】Cactus Reloaded

题目传送门:http://poj.org/problem?id=3567

BZOJ_1023

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<vector>
#include<stack>
#define LL long long
#define abs(x) ((x)>0?(x):-(x))
using namespace std;

const int N = 100000+9;
const int M = N*4;

int head[N],to[M],nxt[M],cost[M],g[N],vout,cnt;
int n,m,ring_cnt,num[N],nod_cnt,que[N*2],frt,bak=1;
vector<int> rings[M];

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar();
	return ret*f;
}

inline void Add_Edge(int u, int v, int w){
	static int T = 1;
	to[++T] = v; nxt[T] = head[u]; head[u] = T; cost[T] = w;
	to[++T] = u; nxt[T] = head[v]; head[v] = T; cost[T] = w;
}

void DFS(int w, int f){
	num[w] = ++nod_cnt;
	for (int i=head[w];i;i=nxt[i]) if (to[i] != f) {
		if (!num[to[i]]) que[++frt]=i, DFS(to[i],w) ,frt--;
		else if (num[to[i]] < num[w]) { 
			rings[++ring_cnt].push_back(i);
			for (int j=frt;j;j--) 
				if (to[que[j]] == to[i]) break;
				else rings[ring_cnt].push_back(que[j]);
		}
	}
}

inline void prework(){
	for (int k=1;k<=ring_cnt;k++) {
		int sz = rings[k].size(), top = to[rings[k][0]];
		for (int i=0,tmp;i<sz;i++) {
			tmp = to[rings[k][i]];
			to[rings[k][i]] = to[rings[k][i]^1] = 0;
			rings[k][i] = tmp;
		} Add_Edge(top,++cnt,0);
		for (int i=1;i<sz;i++) Add_Edge(cnt,rings[k][i],min(i,sz-i));
	} 
}

void Get_Ans(int w, int f){
	for (int i=head[w];i;i=nxt[i]) if (to[i] && to[i] != f){
		Get_Ans(to[i],w); 
		if (w <= n) vout = max(vout, g[w] + g[to[i]] + cost[i]);
		g[w] = max(g[w], g[to[i]] + cost[i]);
	}
}

inline void DP(){
	static int buf[N*2];
	for (int k=1;k<=ring_cnt;k++) {
		int sz = rings[k].size(), top = rings[k][0];
		int tmp = g[top]; g[top] = 0; frt = 0; bak = 1;
		for (int i=0;i<sz;i++) buf[i+1] = buf[i+1+sz] = rings[k][i];
		for (int i=1;i<=sz*2;i++) { 
			while (bak <= frt && i-que[bak] > sz/2) bak++;
			if (bak <= frt) vout = max(vout, i+g[buf[i]] + g[buf[que[bak]]]-que[bak]);
			while (bak <= frt && g[buf[que[frt]]]-que[frt] <= g[buf[i]]-i) frt--;
			que[++frt] = i; 
		} g[top] = tmp;
	}
}

int main(){
	cnt = n = read(); m = read();
	for (int len,i=1,tmp;i<=m;i++) {
		len = read(); tmp = read();
		for (int j=1,t;j<len;j++) Add_Edge((t=read()),tmp,1), tmp=t;
	} DFS(1,1); prework(); Get_Ans(1,1); DP(); 
	printf("%d\n",vout);
	return 0;
}

【BZOJ 1023】[SHOI2008] Cactus仙人掌图

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1023
数据生成器:http://paste.ubuntu.com/23106255/
测试数据:http://pan.baidu.com/s/1pLxVyQ3

仙人掌DP,调了好久QAQ
题解的话,让我们召唤CA爷:http://blog.csdn.net/creationaugust/article/details/48028117

值得一提的是,之前我尝试将其缩成一棵树之后,BFS两遍找直径
但wa了很久,很久。后来发现,因为走到环那里时,路径的长度是变化的,且入口不同->长度不同
所以两次BFS是错的,且无法通过多次BFS来保证正确性
换句话说,仙人掌的直径貌似只能这么DP?

另外值得一提的是,仙人掌的预处理的时候,确实只能存边,存点能找到反例(之前拍到过一组,但忘了记录下来)
换一句话来说,popoqqq的仙人掌是可以被卡掉的(好像是几个环连在一个点上)

另外的话,我仙人掌的板之前一直是错的,因为出题人数据太弱+自己数据太弱没有发现,这一份才是对的(能过neerc的数据我还是很自信的)
最后的话,仙人掌的题目,不套模板会快很多,套模板虽然代码风格统一,但写起来确实麻烦一些

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<vector>
#include<stack>
#define LL long long
#define abs(x) ((x)>0?(x):-(x))
using namespace std;

const int N = 100000+9;
const int M = N*4;

int head[N],to[M],nxt[M],cost[M],g[N],vout,cnt;
int n,m,ring_cnt,num[N],nod_cnt,que[N*2],frt,bak=1;
vector<int> rings[M];

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar();
	return ret*f;
}

inline void Add_Edge(int u, int v, int w){
	static int T = 1;
	to[++T] = v; nxt[T] = head[u]; head[u] = T; cost[T] = w;
	to[++T] = u; nxt[T] = head[v]; head[v] = T; cost[T] = w;
}

void DFS(int w, int f){
	num[w] = ++nod_cnt;
	for (int i=head[w];i;i=nxt[i]) if (to[i] != f) {
		if (!num[to[i]]) que[++frt]=i, DFS(to[i],w) ,frt--;
		else if (num[to[i]] < num[w]) { 
			rings[++ring_cnt].push_back(i);
			for (int j=frt;j;j--) 
				if (to[que[j]] == to[i]) break;
				else rings[ring_cnt].push_back(que[j]);
		}
	}
}

inline void prework(){
	for (int k=1;k<=ring_cnt;k++) {
		int sz = rings[k].size(), top = to[rings[k][0]];
		for (int i=0,tmp;i<sz;i++) {
			tmp = to[rings[k][i]];
			to[rings[k][i]] = to[rings[k][i]^1] = 0;
			rings[k][i] = tmp;
		} Add_Edge(top,++cnt,0);
		for (int i=1;i<sz;i++) Add_Edge(cnt,rings[k][i],min(i,sz-i));
	} 
}

void Get_Ans(int w, int f){
	for (int i=head[w];i;i=nxt[i]) if (to[i] && to[i] != f){
		Get_Ans(to[i],w); 
		if (w <= n) vout = max(vout, g[w] + g[to[i]] + cost[i]);
		g[w] = max(g[w], g[to[i]] + cost[i]);
	}
}

inline void DP(){
	static int buf[N*2];
	for (int k=1;k<=ring_cnt;k++) {
		int sz = rings[k].size(), top = rings[k][0];
		int tmp = g[top]; g[top] = 0; frt = 0; bak = 1;
		for (int i=0;i<sz;i++) buf[i+1] = buf[i+1+sz] = rings[k][i];
		for (int i=1;i<=sz*2;i++) { 
			while (bak <= frt && i-que[bak] > sz/2) bak++;
			if (bak <= frt) vout = max(vout, i+g[buf[i]] + g[buf[que[bak]]]-que[bak]);
			while (bak <= frt && g[buf[que[frt]]]-que[frt] <= g[buf[i]]-i) frt--;
			que[++frt] = i; 
		} g[top] = tmp;
	}
}

int main(){
	cnt = n = read(); m = read();
	for (int len,i=1,tmp;i<=m;i++) {
		len = read(); tmp = read();
		for (int j=1,t;j<len;j++) Add_Edge((t=read()),tmp,1), tmp=t;
	} DFS(1,1); prework(); Get_Ans(1,1); DP(); 
	printf("%d\n",vout);
	return 0;
}

【BZOJ 3047】Freda的传呼机

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3047

这题应该是基环树?然而不会基环树的做法QAQ
所以我们把他当成仙人掌来做吧! (*^_^*)

#include<iostream>
#include<cstdio>
#include<vector>
#define abs(x) ((x)<0?-(x):(x))
using namespace std;
 
const int MAXN = 40000;
 
int n,m,q,nxt[MAXN],to[MAXN],head[MAXN],que[MAXN];
int fa[MAXN][20],num[MAXN],lowlink[MAXN],ring_cnt,ring_num[MAXN];
int ori[MAXN],dep[MAXN],dis[MAXN],cost[MAXN],length[MAXN];
vector<int> rings[MAXN];
 
inline int read(){
    char c=getchar(); int buf=0,f=1;
    while (c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while (c<='9'&&c>='0'){buf=buf*10+c-'0';c=getchar();}
    return buf*f;
}
 
inline void AddEdge(int u, int v, int c){
    static int T = 0;
    to[++T] = v; nxt[T] = head[u]; head[u] = T; cost[T] = c;
    to[++T] = u; nxt[T] = head[v]; head[v] = T; cost[T] = c;
}
 
void DFS(int w, int f){
    static int T = 0, tot = 0;
    num[w] = lowlink[w] = ++T; que[++tot] = w;
    for (int i=head[w];i;i=nxt[i]) if (to[i] != f) {
        if (!num[to[i]]) {
            DFS(to[i],w);
            if (lowlink[to[i]] > num[w]) tot--;
            else if (lowlink[to[i]] < num[w]) lowlink[w] = lowlink[to[i]];
            else {
                ring_cnt++; 
                while (que[tot] != w) rings[ring_cnt].push_back(que[tot--]);
                rings[ring_cnt].push_back(w); 
            }
        } else lowlink[w] = min(lowlink[w],num[to[i]]);
    }
}
 
inline void prework(){
    for (int k=1;k<=ring_cnt;k++) {
        int sz = rings[k].size();
        int rt = rings[k][sz-1],tmp=0;
        for (int i=1;i<=sz;i++) que[i] = rings[k][i-1];
        que[0] = rt; que[sz+1] = que[1];
         
        for (int w=1;w<=sz;w++) {
            for (int i=head[que[w]];i;i=nxt[i])
                if (to[i] == que[w-1]) to[i] = 0, tmp += cost[i];
                else if (to[i] == que[w+1]) to[i] = 0;
            ori[que[w]] = tmp;
        } ori[rt] = 0; length[k] = tmp;
        for (int i=1;i<sz;i++) ring_num[que[i]] = k;
        for (int i=1;i<sz;i++) AddEdge(rt,que[i],min(ori[que[i]],tmp-ori[que[i]]));
    }
}
 
void GetDis(int w, int f){
    fa[w][0] = f; dep[w] = dep[f] + 1; 
    for (int i=head[w];i;i=nxt[i]) if (!dep[to[i]] && to[i])
        dis[to[i]] = dis[w] + cost[i], GetDis(to[i],w);
}
 
inline void LCA_init(){
    for (int k=1;k<=18;k++) for (int i=1;i<=n;i++) 
        fa[i][k] = fa[fa[i][k-1]][k-1]; 
}
 
inline int LCA(int a, int b, int &l1, int &l2) {
    if (dep[a] < dep[b]) swap(a,b);
    for (int k=18;~k;k--) if (dep[fa[a][k]] >= dep[b])
        a = fa[a][k];
    l1 = l2 = a;
    if (a == b) return a;
    for (int k=18;~k;k--) if (fa[a][k] != fa[b][k])
        a = fa[a][k], b = fa[b][k];
    l1 = a; l2 = b; return fa[a][0];
}
 
int  main(){
    n = read(); m = read(); q = read();
    for (int i=1,a,b,c;i<=m;i++) a = read(), b = read(), c = read(), AddEdge(a, b, c);  
    DFS(1,0); prework(); dep[1] = 1; GetDis(1,0); LCA_init();
     
    for (int i=1,u,v,l1,l2;i<=q;i++){
        u = read(), v = read();
        int lca = LCA(u, v, l1, l2);
        if (ring_num[l1] && ring_num[l1] == ring_num[l2]) {
            int len = abs(ori[l1] - ori[l2]);
            len = min(length[ring_num[l2]]-len,len);
            printf("%d\n",dis[u]+dis[v]-dis[l1]-dis[l2]+len);
        } else printf("%d\n",dis[u]+dis[v]-2*dis[lca]);
    }
    return 0;
} 

同样,这份代码的预处理部分仍然是错的
正确的请参见BZOJ_1023

【BZOJ 2125】最短路

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2125

仙人掌上求最短路
先搞成树,然后树怎么搞,这题就怎么搞

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstdlib>
using namespace std;
 
const int MAXN = 10000+9;
 
int n,m,Q,lowlink[MAXN],num[MAXN],dis[MAXN],nxt[MAXN*4],to[MAXN*4],head[MAXN],cost[MAXN*4];
int ring_cnt,fa[MAXN][16],dep[MAXN],length[MAXN],ring_num[MAXN],ori[MAXN],que[MAXN],fro;
vector<int> rings[MAXN];
 
inline int read(){
    char c=getchar(); int ret=0,f=1;
    while (c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while (c<='9'&&c>='0'){ret=ret*10+c-'0';c=getchar();}
    return ret*f;
}
 
inline void AddEdge(int a, int b, int c){
    static int T = 0;
    to[++T] = b; nxt[T] = head[a]; head[a] = T; cost[T] = c;
    to[++T] = a; nxt[T] = head[b]; head[b] = T; cost[T] = c;
}
 
void DFS(int w, int f){
    static int T = 0; fa[w][0] = f;
    lowlink[w] = num[w] = ++T; que[++fro] = w;
    for (int i=head[w];i;i=nxt[i]) if (to[i] != f) {
        if (!num[to[i]]) {
            DFS(to[i], w);
            if (lowlink[to[i]] > num[w]) fro--;
            else if (lowlink[to[i]] < num[w]) lowlink[w] = lowlink[to[i]];
            else {
                ring_cnt++; int nw = que[fro--];
                while (nw != w) {
                    rings[ring_cnt].push_back(nw);
                    nw = que[fro--];
                }   
                rings[ring_cnt].push_back(w); que[++fro] = w;
            }
        } else lowlink[w] = min(lowlink[w], num[to[i]]);
    }
}
 
inline void prework(){
    for (int k=1;k<=ring_cnt;k++) {
        int size = rings[k].size();
        int rt = rings[k][size-1],tmp=0;
         
        for (int i=1;i<=size;i++) que[i] = rings[k][i-1];
        que[0] = rt; que[size+1] = que[1];
        for (int i=1;i<size;i++) ring_num[que[i]] = k, fa[que[i]][0] = rt;
         
        for (int i=1;i<=size;i++) {
            for (int j=head[que[i]];j;j=nxt[j]) {
                if (to[j] == que[i-1]) to[j] = 0, tmp += cost[j];
                else if (to[j] == que[i+1]) to[j] = 0;
            }
            ori[que[i]] = tmp;
        } ori[rt] = 0; length[k] = tmp;
        for (int i=1;i<size;i++) AddEdge(rt,que[i],min(ori[que[i]], tmp-ori[que[i]]));
    }
}
 
void GetDis(int w){
    for (int i=head[w];i;i=nxt[i]) if (!dis[to[i]] && to[i]) 
        dis[to[i]]=dis[w]+cost[i], dep[to[i]] = dep[w] + 1, GetDis(to[i]);
}
 
inline void LCA_init() {
    for (int k=1;k<=15;k++) for (int i=1;i<=n;i++) 
        fa[i][k] = fa[fa[i][k-1]][k-1];
}
 
inline int LCA(int a, int b, int &l1, int &l2){
    if (dep[a] < dep[b]) swap(a,b);
    for (int k=15;~k;k--) if (dep[fa[a][k]] >= dep[b]) a = fa[a][k];
    l1 = l2 = a;
    if (a == b) return a;
    else {
        for (int k=15;~k;k--) if (fa[a][k] != fa[b][k]) a = fa[a][k], b = fa[b][k];
        l1 = a, l2 = b; return fa[a][0];
    }
}
 
int main(){
    n = read(); m = read(); Q = read();
    for (int i=1,a,b,c;i<=m;i++) 
        a = read(), b = read(), c = read(),
        AddEdge(a, b, c);
    DFS(1,0); prework();
    dis[1] = dep[1] = 1; GetDis(1); LCA_init();
    for (int i=1,a,b,r,lca1,lca2;i<=Q;i++){
        a = read(); b = read();
        r = LCA(a, b, lca1, lca2);
        if (ring_num[lca1] && ring_num[lca1] == ring_num[lca2]) {
            int len = abs(ori[lca1]-ori[lca2]);
            len = min(len, length[ring_num[lca1]]-len);
            printf("%d\n",dis[a]-dis[lca1]+dis[b]-dis[lca2]+len);
        } else printf("%d\n",dis[a]+dis[b]-2*dis[r]);
    }
    return 0;
}

另外,这份代码在预处理环那里有问题
会被一个点连了几个环的情况卡成翔
正确的写法,参见BZOJ_1023

【Codeforces 708C】Centroids

题目传送门:http://codeforces.com/problemset/problem/708/C

我是函数式线段树+出栈入栈序的做法(其实一个RMQ即可)
但这题可以O(n)DP!记录子树中,最大的,不超过n/2的值是多少

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define LL long long
#define abs(x) ((x)>0?(x):-(x))
using namespace std;

const int N = 800000+9;
const int M = 20000000;
const int INF = 1000000000;

int n,tot[N],ls[N],sig,rs[N],head[N],to[N],nxt[N],vex[N*2];

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar();
	return ret*f;
}

namespace Chair_Tree{
	#define CT Chair_Tree
	int sum[M][2],ch[M][2],cnt,root[N],ans_tmp;
	
	void Add(int p, int &w, int l, int r, int pos, int ty) {
		w = ++cnt; ch[w][0] = ch[p][0]; ch[w][1] = ch[p][1]; 
		sum[w][0] = sum[p][0]; sum[w][1] = sum[p][1]; sum[w][ty]++;
		if (l < r) {
			int mid = l + r + 1 >> 1;
			if (pos < mid) Add(ch[p][0],ch[w][0],l,mid-1,pos,ty);
			else Add(ch[p][1],ch[w][1],mid,r,pos,ty);
		}
	} 
	
	inline void modify(int i, int val) {
		if (val > 0) Add(root[i-1],root[i],1,n,val,1);
		else Add(root[i-1],root[i],1,n,-val,0);
	}
	
	void query(int w, int l, int r, int L, int R, int f, int ty){
		if (L <= l && r <= R) ans_tmp += sum[w][ty]*f;
		else {
			int mid = l + r + 1 >> 1;
			if (L < mid) query(ch[w][0],l,mid-1,L,R,f,ty);
			if (R >= mid) query(ch[w][1],mid,r,L,R,f,ty);
		}
	}
	
	inline int query(int l, int r, int L, int R,int ty) {
		if (l > r) return false;
		ans_tmp = 0;
		query(root[l-1],1,n,L,R,-1,ty);
		query(root[r],1,n,L,R,1,ty);
		return ans_tmp;
	}
	
	inline void Merge(){
		for (int i=1;i<=cnt;i++) 
			sum[i][0] = sum[i][1] - sum[i][0];
	}
};

void DFS(int w, int f) {
	tot[w] = 1; ls[w] = ++sig; 
	for (int i=head[w];i;i=nxt[i]) if (to[i] != f)
		DFS(to[i],w), tot[w] += tot[to[i]];
	rs[w] = ++sig; 
	vex[ls[w]] = tot[w];
	vex[rs[w]] = -tot[w];
}

inline void AddEdge(int a, int b) {
	static int T = 0;
	to[++T] = b; nxt[T] = head[a]; head[a] = T;
	to[++T] = a; nxt[T] = head[b]; head[b] = T;
}

inline bool judge(int w) {
	int MX = 0, vx = 0, MN, ty, sta;
	for (int i=head[w];i;i=nxt[i]) 
		if (ls[w] < ls[to[i]] && ls[to[i]] <= rs[w]) {if (MX < tot[to[i]]) MX = tot[to[i]], vx = to[i], ty = 0;} 
		else {int tmp = n - tot[w]; if (MX < tmp) MX = tmp, vx = to[i], ty = 1;}
	if (MX <= n/2) return 1;
	else {
		sta = n/2;
		MN = MX-n/2;
		if (!ty) return CT::query(ls[vx],rs[vx],MN,sta,1);
		else {
			int tmp = CT::query(1,ls[w]-1,MN,sta,1) + CT::query(rs[w]+1,n*2,MN,sta,1);
			tmp -= CT::query(1,ls[w]-1,MN,sta,0); MN = n - MN; sta = n - sta; swap(MN,sta);
			tmp += CT::query(2,ls[w],MN,sta,0);
			return tmp;
		}
	}
}

int main(){
	n = read(); 
	for (int i=1;i<n;i++) AddEdge(read(),read()); DFS(1,1);
	for (int i=1;i<=n*2;i++) CT::modify(i,vex[i]); CT::Merge();
	for (int i=1;i<=n;i++) printf("%d ",judge(i));
	return 0;
}

【Codeforces 700B】Connecting Universities

题目传送门:http://codeforces.com/problemset/problem/700/B
中文体面:http://paste.ubuntu.com/23084330/

好题啊!做了以后心旷神怡!巧啊!妙啊!
题解的话,让我们来召唤官方题解:http://codeforces.com/blog/entry/46283

#include<iostream>
#include<cstdio>
#define LL long long
using namespace std;

const int N = 400000+9;

int n,k,tot,head[N],to[N],nxt[N],tag[N],f[N];
LL vout;

inline void Add_Edge(int a, int b){
	static int T = 0;
	nxt[++T] = head[a]; to[T] = b; head[a] = T;
	nxt[++T] = head[b]; to[T] = a; head[b] = T;
}

inline int read(){
	char c=getchar(); int ret=0;
	while (c<'0'||c>'9') c=getchar();
	while (c<='9'&&c>='0') ret=ret*10+c-'0', c=getchar();
	return ret;
}

void DFS(int w, int fa) {
	f[w] = tot; if (tag[w]) tot++;
	for (int i=head[w];i;i=nxt[i]) 
		if (to[i] != fa) DFS(to[i], w);
	f[w] = tot - f[w];
}

int main(){
	n = read(); k = read();
	for (int i=1,lim=k*2,a,b;i<=lim;i++) tag[read()] = 1;
	for (int i=1;i<n;i++) Add_Edge(read(),read());
	DFS(1,1); for (int i=2;i<=n;i++) vout += min(f[i],2*k-f[i]);
	printf("%I64d\n",vout); 
	return 0;
} 

【BZOJ 3943】[Usaco2015 Feb] SuperBull

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3943
离线版题目:http://paste.ubuntu.com/23080229/

XOR意义下的位运算生成树

#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;

const int N = 2000+9;
const int M = N*32;
const int SGZ = 2;

int n,arr[N],fa[N],to[N],val[N];
LL vout;

namespace Trie{
	int MX[M],MN[M],ch[M][SGZ],cnt,root,NUM,COL,ans_tmp;
	
	inline void init(){
		memset(MX,-1,sizeof(MX));
		memset(MN,-1,sizeof(MN));
		memset(ch,0,sizeof(ch));
		cnt = root = 0;
	}
	
	void Insert(int &w, int t){
		if (!w) w = ++cnt; 
		if (!~MX[w] || MX[w] < COL) MX[w] = COL;
		if (!~MN[w] || MN[w] > COL) MN[w] = COL;
		if (t) Insert(ch[w][(NUM&t)>0],t>>1);  
	}
	
	inline void insert(int num, int col){
		if (!root) root = ++cnt;
		NUM = num, COL = col;
		Insert(ch[root][(num&(1<<30))>0],1<<29);
	}
	
	void Query(int w, int t){
		if (t) {
			int to = ch[w][((NUM&t)>0)^1];
			if (to && (MX[to] != COL || MN[to] != COL)) ans_tmp |= t, Query(to,t>>1);
			else Query(ch[w][(NUM&t)>0],t>>1);
 		} else {
			if (COL != MN[w]) COL = MN[w];
			else COL = MX[w]; 	
		}
	} 
	
	inline pair<int,int> query(int num, int col){
		ans_tmp = 0; NUM = num; COL = col;
		Query(root,1<<30);
		return make_pair(ans_tmp,COL);
	}
};

inline int find(int w){
	int f=fa[w],tmp;
	while (fa[f] != f) f = fa[f];
	while (w != f) tmp = fa[w], fa[w] = f, w = tmp;
	return f;
}

inline int read(){
	char c=getchar(); int ret=0;
	while (c<'0'||c>'9') c=getchar();
	while (c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar();
	return ret; 
}

int main(){
	n = read(); int cnt = n-1;
	for (int i=1;i<=n;i++) arr[i] = read(), fa[i] = i;
	while (cnt) {
		Trie::init(); 
		memset(val,0,sizeof(val));
		for (int i=1;i<=n;i++) Trie::insert(arr[i],find(i));
		for (int i=1;i<=n;i++) {
			pair<int,int> tmp = Trie::query(arr[i],fa[i]);
			if (tmp.first > val[fa[i]]) val[fa[i]] = tmp.first, to[fa[i]] = tmp.second;
		}
		for (int i=1;i<=n;i++) if (to[i] && find(to[i]) != find(i)) 
			vout += val[i], fa[find(i)] = find(to[i]), cnt--;
	}
	printf("%lld\n",vout);
	return 0;
}

不过貌似这个题直接暴力就行了?

【HDU 5627】Clarke and MST

题目传送门:http://acm.split.hdu.edu.cn/showproblem.php?pid=5627
吐槽:HDU你好好的,为什么要加一个split?你TM还会分身了?

本来以为是位运算生成树,结果不是QAQ
管他的,看都看到了就来练练手
结果居然被这种题目给卡常了,cin在HDU上真是慢得一逼╮(╯▽╰)╭

#include<iostream>
#include<cstdio>
using namespace std;

const int N = 300000+9;

struct Edge{int u,v,val;}e[N];
int n,m,fa[N];

inline int find(int w){
	int f=fa[w],tmp;
	while (f != fa[f]) f = fa[f];
	while (w != f) tmp = fa[w], fa[w] = f, w = tmp;
	return f;
}

inline bool test(int x) {
	x = 1<<x-1; int tag = n-1;
	for (int i=1;i<=n;i++) fa[i] = i;
	for (int i=1;i<=m;i++) if (e[i].val & x) 
		if (find(e[i].u) == find(e[i].v)) continue;
		else fa[find(e[i].u)] = find(e[i].v), tag--;
	if (!tag) { 
		int tmp=0;
		for (int i=1;i<=m;i++) if (e[i].val & x) e[++tmp] = e[i];
		m = tmp; return true;
	} else return false;
}

inline int read(){
	char c=getchar(); int ret=0;
	while (c<'0'||c>'9') c=getchar();
	while (c<='9'&&c>='0') ret=ret*10+c-'0', c=getchar();
	return ret;
}

int main(){
	int T; cin>>T; while (T--) {
		cin>>n>>m; int tag = n-1, vout = 0;
		for (int i=1;i<=n;i++) fa[i] = i;
		for (int i=1;i<=m;i++) {
			e[i].u = read(); e[i].v = read(); e[i].val = read();
			if (find(e[i].u) == find(e[i].v)) continue;
			else fa[find(e[i].u)] = fa[find(e[i].v)], tag--;
		}
		if (tag) printf("0\n");
		else {
			for (int i=31;i;i--) if (test(i)) vout |= 1<<i-1;
			printf("%d\n",vout);
		}
	}
	return 0;
}

【UOJ 176】新年的繁荣

题目传送门:http://uoj.ac/problem/176

位运算生成树系列之&最大
详细情况,见之后的算法笔记吧

#include<iostream>
#include<cstdio>
#define LL long long
using namespace std;

const int N = 1<<19;

int n,m,num[N],fa[N];
LL vout;

inline int read(){
	char c=getchar(); int ret=0;
	while (c<'0'||c>'9') c=getchar();
	while (c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar();
	return ret;
}

inline int find(int w){
	int f=fa[w],tmp;
	while (f != fa[f]) f = fa[f];
	while (w != f) tmp=fa[w],fa[w]=f,w=tmp;
	return f;
}

int main(){
	n = read(); m = read();
	for (int i=1,v;i<=n;i++) 
		if (num[v=read()]) vout += v;
		else num[v] = v;
	for (int q=(1<<m)-1;q;q--) { fa[q] = q;
		for (int i=1;i<=m && !num[q];i++) num[q] = num[q|1<<i-1];
		for (int i=1,f1,f2,u=num[q],v;i<=m;i++) 
			if ((v=num[q|1<<i-1]) && find(u) != find(v)) vout += q, fa[find(u)] = find(v);
	}
	printf("%lld\n",vout);
	return 0;
}

来补一个Boruvka + trie的版本:

#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
#define R(x) ((x)>0)
using namespace std;

const int N = 100000+9;
const int M = N*32*4;

int n,arr[N],v[N],sur[N],fa[N],m;
LL vout = 0;

namespace Trie{
	int root,ch[M][2],MX[M],MN[M],cnt,NUM,COL,ans_tmp;
	
	inline void init(){
		memset(ch,0,sizeof(ch));
		memset(MX,0,sizeof(MX));
		memset(MN,0,sizeof(MN));
		cnt = root = 1;
	}
	
	void Insert(int &w, int t){
		if (!w) w = ++cnt;
		if (!MX[w] || MX[w] < COL) MX[w] = COL;
		if (!MN[w] || MN[w] > COL) MN[w] = COL;
		if (t) Insert(ch[w][R(NUM&t)],t>>1);
	}
	
	inline void insert(int num, int col){
		NUM = num; COL = col;
		Insert(ch[root][R(num&(1<<m))],1<<m-1);
	}
	
	void Merge(int &w, int p){
		if (!w) w = ++cnt;
		if (!MX[w] || MX[w] < MX[p]) MX[w] = MX[p];
		if (!MN[w] || MN[w] > MN[p]) MN[w] = MN[p];
		
		if (ch[p][0]) Merge(ch[w][0], ch[p][0]);
		if (ch[p][1]) Merge(ch[w][1], ch[p][1]);
	}
	
	void merge(int w){
		if (ch[w][1]) Merge(ch[w][0],ch[w][1]), merge(ch[w][1]);
		if (ch[w][0]) merge(ch[w][0]); 
	}
	
	void Query(int w, int t){
		if (t) {
			if (R(NUM&t) && (MN[ch[w][1]] != COL || MX[ch[w][1]] != COL)) ans_tmp|=t, Query(ch[w][1], t>>1); 
			else Query(ch[w][0],t>>1);
		} else {
			if (MN[w] != COL) COL = MN[w];
			else COL = MX[w];
		}
	}
	
	inline pair<int,int> query(int num, int col){
		ans_tmp = 0; NUM = num, COL = col;
		Query(root,1<<m);
		return make_pair(ans_tmp,COL);
	}
};

inline int find(int w){
	int f=fa[w], tmp;
	while (fa[f] != f) f = fa[f];
	while (w != f) tmp=fa[w],fa[w]=f,w=tmp;
	return f;
}

inline int read(){
	char c=getchar(); int ret=0;
	while (c<'0'||c>'9') c=getchar();
	while (c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar();
	return ret;
}

int main(){
	n = read(); m = read(); int cnt = n-1;
	for (int i=1;i<=n;i++) fa[i] = i, arr[i] = read();
	while (cnt) {
		Trie::init(); memset(v,-1,sizeof(v));
		for (int i=1;i<=n;i++) Trie::insert(arr[i],find(i));
		Trie::merge(Trie::root);
		for (int i=1;i<=n;i++) {
			pair<int,int> tmp = Trie::query(arr[i],fa[i]);
			if (tmp.first > v[fa[i]]) v[fa[i]] = tmp.first, sur[fa[i]] = tmp.second;
		}
		for (int i=1;i<=n;i++) if (~v[i] && find(i) != find(sur[i])) 
			vout += v[i], fa[find(i)] = find(sur[i]), cnt--;
	}
	printf("%lld\n",vout);
	return 0;
}

【POJ 3522】Slim Span

题目传送门:http://poj.org/problem?id=3522

今天在水位运算生成树,无聊来水一水
很暴力,枚举最大边,然后搞一搞,并查集判树

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

const int N = 100+9;
const int M = 10000+9;
const int INF = 1000000000;

struct Edge{int u,v,w;}e[M];
int n,m,vout,fa[N];

inline bool operator < (const Edge &A, const Edge &B) {return A.w < B.w;}

inline int read(){
	char c=getchar(); int ret=0;
	while (c<'0'||c>'9') c=getchar();
	while (c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar();
	return ret;
}

inline int find(int w){
	int f=fa[w],tmp;
	while (fa[f] != f) f = fa[f];
	while (w != f) tmp=fa[w], fa[w]=f, w=tmp;
	return f;
}

inline int Get_Ans(int lim){
	for (int i=1;i<=n;i++) fa[i] = i;
	fa[e[lim].u] = fa[e[lim].v];
	for (int i=lim-1,t=n-2,f1,f2;i;i--) {
		f1 = find(e[i].u); f2 = find(e[i].v);
		if (f1 != f2) fa[f2] = f1, t--;
		if (!t) return e[i].w;
	}
	return -INF;
}

int main(){
	while (scanf("%d%d",&n,&m) && (n||m)) {
		for (int i=1;i<=m;i++) e[i].u = read(), e[i].v = read(), e[i].w = read();
		sort(e+1,e+1+m); vout = INF;
		for (int lim=m;lim>=n-1;lim--) 
			vout = min(vout, e[lim].w - Get_Ans(lim));
		if (n <= 2) printf("0\n");
		else printf("%d\n",vout>=INF?-1:vout);	
	}
	return 0;
}

【BZOJ 2429】[HAOI2006] 聪明的猴子

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2429

MST的板题,MST搞出来,再拿每一个猴子比一比就好
好吧,其实我是来练Boruvka的……

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define pow(x) ((x)*(x))
using namespace std;

const int N = 1000+9;
const int M = 1000000+9;
const double EPS = 1e-8;

int X[N],Y[N],to[M],nxt[M],head[N],val[N],tot,n,m,vout,fa[N],cot[N];
double w[M],cpt[N],MX;

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}

inline void AddEdge(int i, int j){
	double tmp = sqrt(pow(X[i]-X[j])+pow(Y[i]-Y[j]));
	to[++m] = j; nxt[m] = head[i]; w[m] = tmp; head[i] = m;
	to[++m] = i; nxt[m] = head[j]; w[m] = tmp; head[j] = m;
}

inline int find(int w){
	int f=fa[w],tmp;
	while (fa[f] != f) f = fa[f];
	while (w != f) tmp=fa[w],fa[w]=f,w=tmp;
	return f;
}

inline void Boruvka(){
	for (int i=1;i<=n;i++) fa[i] = i;
	for (int t=n;t>1;) {
		memset(cot,0,sizeof(cot));
		for (int i=1,f1,f2;i<=n;i++) for (int j=head[i];j;j=nxt[j]){
			f1 = find(i); f2 = find(to[j]);
			if (f1 != f2) {
				if (!cot[f1] || cpt[f1] > w[j]) cot[f1] = f2, cpt[f1] = w[j];
				if (!cot[f2] || cpt[f2] > w[j]) cot[f2] = f1, cpt[f2] = w[j];
			} 	
		}
		
		for (int i=1,ft;i<=n;i++) if (fa[i] == i) {
			if ((ft = find(cot[i])) != i) {
				fa[ft] = i; MX = max(MX, cpt[i]); t--;
			}
		} 
	}
}

int main(){
	tot=read(); for (int i=1;i<=tot;i++) val[i] = read();
	n = read(); for (int i=1;i<=n;i++) {
		X[i] = read(); Y[i] = read();
		for (int j=1;j<i;j++) AddEdge(i,j);
	}
	Boruvka();
	for (int i=1;i<=tot;i++) if (EPS+val[i] >= MX) vout++;
	printf("%d\n",vout);
	return 0;
}

【模板库】Borůvka’s algorithm

参考例题:http://www.lydsy.com/JudgeOnline/problem.php?id=2429
参考代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define pow(x) ((x)*(x))
using namespace std;

const int N = 1000+9;
const int M = 1000000+9;
const double EPS = 1e-8;

int X[N],Y[N],to[M],nxt[M],head[N],val[N],tot,n,m,vout,fa[N],cot[N];
double w[M],cpt[N],MX;

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}

inline void AddEdge(int i, int j){
	double tmp = sqrt(pow(X[i]-X[j])+pow(Y[i]-Y[j]));
	to[++m] = j; nxt[m] = head[i]; w[m] = tmp; head[i] = m;
	to[++m] = i; nxt[m] = head[j]; w[m] = tmp; head[j] = m;
}

inline int find(int w){
	int f=fa[w],tmp;
	while (fa[f] != f) f = fa[f];
	while (w != f) tmp=fa[w],fa[w]=f,w=tmp;
	return f;
}

inline void Boruvka(){
	for (int i=1;i<=n;i++) fa[i] = i;
	for (int t=n;t>1;) {
		memset(cot,0,sizeof(cot));
		for (int i=1,f1,f2;i<=n;i++) for (int j=head[i];j;j=nxt[j]){
			f1 = find(i); f2 = find(to[j]);
			if (f1 != f2) {
				if (!cot[f1] || cpt[f1] > w[j]) cot[f1] = f2, cpt[f1] = w[j];
				if (!cot[f2] || cpt[f2] > w[j]) cot[f2] = f1, cpt[f2] = w[j];
			} 	
		}
		
		for (int i=1,ft;i<=n;i++) if (fa[i] == i) {
			if ((ft = find(cot[i])) != i) {
				fa[ft] = i; MX = max(MX, cpt[i]); t--;
			}
		} 
	}
}

int main(){
	tot=read(); for (int i=1;i<=tot;i++) val[i] = read();
	n = read(); for (int i=1;i<=n;i++) {
		X[i] = read(); Y[i] = read();
		for (int j=1;j<i;j++) AddEdge(i,j);
	}
	Boruvka();
	for (int i=1;i<=tot;i++) if (EPS+val[i] >= MX) vout++;
	printf("%d\n",vout);
	return 0;
}

算法简介:https://en.wikipedia.org/wiki/Bor%C5%AFvka%27s_algorithm
算法正确性证明:http://oi.cyo.ng/wp-content/uploads/2016/08/02345678641.png
国外带注释模板:http://www.geeksforgeeks.org/greedy-algorithms-set-9-boruvkas-algorithm/