被部分分误导了。/fn
也要考虑比赛时碰到这种情况怎么办,”回撤“思路的能力要提升,不能拘泥于当前想的方向中。
正解思路比较直接。
考虑从一个点开始拓展时,记 ,然后把包含  的  的一个连通块拓展了,然后将
置为该连通块的点权和,然后继续拓展下去。
容易发现拓展两次之后 
的值就会翻倍。所以这样的拓展只有  次。
每次拓展时,我们需要查询包含 
的,点权 
的一个连通块的信息。使用 kruskal 重构树 + 倍增即可。单次查询是  的。
所以本题就做完了,时间复杂度 。(其中 
为值域)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
   | #include <bits/stdc++.h> using namespace std; #define FI first #define SE second #define mkp make_pair const int N = 4e5 + 5,Lg = 20; typedef long long ll; int n,m,s[N]; vector<int> G[N]; struct Edge { 	int u,v,w; 	Edge(){} 	bool operator < (const Edge &rhs) const { return w < rhs.w;} } e[N]; int val[N],anc[Lg][N]; ll wsze[N]; int ufs[N]; int find(int x) { return ufs[x] == x ? x : ufs[x] = find(ufs[x]);} int main() { 	cin >> n >> m; 	for(int i = 1;i <= n;i++) cin >> s[i],wsze[i] = s[i]; 	for(int i = 1;i <= m;i++) 		cin >> e[i].u >> e[i].v,e[i].w = max(s[e[i].u],s[e[i].v]); 	sort(e + 1,e + m + 1); 	for(int i = 1;i <= n + n;i++) ufs[i] = i; 	int tot = n; 	for(int i = 1;i <= m;i++) 		if(find(e[i].u) != find(e[i].v)) { 			int p = ++tot,lc = find(e[i].u),rc = find(e[i].v); 			val[p] = e[i].w;ufs[lc] = ufs[rc] = p; 			anc[0][lc] = anc[0][rc] = p;wsze[p] = wsze[lc] + wsze[rc]; 		} 	for(int j = 1;j < Lg;j++) 		for(int i = 1;i <= tot;i++) anc[j][i] = anc[j - 1][anc[j - 1][i]]; 	for(int i = 1;i <= n;i++) { 		ll now = s[i]; 		int x = i; 		while(true) { 			for(int j = Lg - 1;j >= 0;j--) 				if(anc[j][x] && val[anc[j][x]] <= now) x = anc[j][x]; 			if(wsze[x] == now) break; 			now = wsze[x]; 		} 		if(!anc[0][x]) putchar('1'); 		else putchar('0'); 	} 	return 0; }
 
  |