ZR省选十连测

#1

A

考虑其等价于把\(0\)看作\(<\),\(1\)看作\(>\)求满足条件的排列数量。

可以使用容斥加分治\(FFT\)解决其。

B

太难了,补不动。

C

考虑\(g_{i,j},j \in [0,3]\) 为通过\(i\)往上延伸的\(j\)长度的链的子树内部权值。

这里只考虑\(k = 4\) 的情况。

思考我们需要记录前缀合并情况里\(s_1 > s_3\) 的个数,以及\(s_2\) 的寄偶性。

如果随机序列\(1,-1\) 其前缀和大概率不大于\(\sqrt n\),我们随机打碎儿子,然后记录即可。

点击查看代码
#include <bits/stdc++.h>
#define fi first
#define se second
#define mp make_pair
using namespace std;

typedef pair <int, int> pii;

const long long INF = 0x3f3f3f3f3f3f3f3f;

long long dp[200010][5];
vector <pii> G[200010];
int n, k;
long long f[2010][2];

void dfs(int x, int last) {
	int son = 0;
	for (auto it : G[x]) {
		int v = it.fi, w = it.se;
		if (v == last) continue;
		dfs(v, x), son++;
	}
	int S = min(2001, son << 1 | 1);
	for (int i = 0; i <= S; i++) {
		f[i][0] = f[i][1] = -INF;
	}
	random_shuffle(G[x].begin(), G[x].end());
	int be = S >> 1;
	f[be][0] = 0;
	for (auto it : G[x]) {
		int v = it.fi, w = it.se;
		if (v == last) continue;
		long long lst[2] = {-INF, -INF};
		for (int i = 0; i < S; i++) {
			long long cur[2] = {f[i][0], f[i][1]};
			for (int j = 0; j < 2; j++) {
				f[i][j] += max(dp[v][0], dp[v][k - 1] + w);
				f[i][j] = max(f[i][j], lst[j] + dp[v][0] + w);
				f[i][j] = max(f[i][j], f[i + 1][j] + dp[v][k - 2] + w);
				if (k == 4) f[i][j] = max(f[i][j], cur[j ^ 1] + dp[v][1] + w);
			}
			lst[0] = cur[0], lst[1] = cur[1];
		}
	}
	dp[x][0] = f[be][0];
	dp[x][1] = f[be + 1][0];
	dp[x][k - 1] = be ? f[be - 1][0] : -INF;
	if (k == 4) dp[x][2] = f[be][1];
}

int main() {
	srand((int)time(NULL));
	scanf("%d%d", &n, &k);
	for (int i = 1; i < n; i++) {
		int x, y, w; scanf("%d%d%d", &x, &y, &w);
		G[x].push_back(mp(y, w));
		G[y].push_back(mp(x, w));
	}
	dfs(1, 1);
	printf("%lld\n", dp[1][0]);
	return 0;
}

#4

A

一个诈骗题,考虑对每个点向外BFS寻找非平凡回路,只要\(u \to v\)时,\(v\)在之前遇到过,则该点的答案为\(dis_v\)

点击查看代码
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
const int Maxn=5000;
const int Inf=0x3f3f3f3f;
int n;
std::vector<int> g[Maxn+5];
int dis[Maxn+5];
bool vis[Maxn+5];
int qu[Maxn+5],qu_f,qu_t;
int init_bfs(int S){
	memset(dis,0x3f,sizeof dis);
	memset(vis,0,sizeof vis);
	dis[S]=0,vis[S]=1;
	qu_f=1,qu_t=0;
	qu[++qu_t]=S;
	while(qu_f<=qu_t){
		int u=qu[qu_f++];
		for(int v:g[u]){
			if(!vis[v]){
				dis[v]=dis[u]+1;
				vis[v]=1;
				qu[++qu_t]=v;
			}
			else if(dis[u]<=dis[v]){
				return dis[u]+1;
			}
		}
	}
	return -1;
}
int main(){
	scanf("%d",&n);
	for(int i=2;i<=n;i++){
		static char s[Maxn+5];
		scanf("%s",s+1);
		int len=0;
		while(s[++len]!='\0');
		len--;
		for(int j=1;j<i;j++){
			if(s[j]=='1'){
				g[i].push_back(j),g[j].push_back(i);
			}
		}
	}
	for(int i=1;i<=n;i++){
		printf("%d\n",init_bfs(i));
	}
	return 0;
}

B

image

#5

A

写做\(f_i = \sum (mex(j,i) * f_{j - 1})\)

考虑我们只要每次每次后缀加入一个数动态维护\(mex\)即可,考虑我们每次加入后缀时,直接查询在\(mex = a_i\)\([l,r]\)里查询前缀\(mex\)的位置,并直接更新其前缀为\(mex <- mex+1\),考虑每次都把对应位置的\(mex\)变为大于其权值,一个数只会变一次,所以复杂度有所保证。

B

考虑有结论

结论一:当\(a < b < c\),有\(a \oplus c >= \min(a \oplus b,b\oplus c)\),那么即最小值异或对只会出现在相邻的\(i,j\)

结论二:当\(b >= a\),\(a \oplus a >= b - a\),当有这两个结论时我们就可以做\(sub1,sub2,sub3\)

我们考虑当\(w > \min (a_i - a_j)\)时,可以构造出答案为\(\min(a_i - a_j)\),那么我们只要每次都枚举小于\(\min(a_i - a_j)\)\(x\),记录其答案即可。

考虑实际上加入一个数时其\(mn\)递减,那么只要枚举时对新加入的对统计即可,那么复杂度为\(O(\sum \frac{2^V}{i}) = O(V2^V)\)

点击查看代码

//晦暗的宇宙,我们找不到光,看不见尽头,但我们永远都不会被黑色打倒。——Quinn葵因
#include<bits/stdc++.h>
#define ll long long
#define N 1000005

using std::set;

set<int>A;

int V,n;

#define inf (1ll << 21)

int mn = inf;

ll ans[inf];

int main(){
	scanf("%d%d",&V,&n);
	for(int i = 0;i < inf;++i)
	ans[i] = inf;
	for(int i = 1;i <= n;++i){
		int opt,x;
		scanf("%d%d",&opt,&x);
		if(opt == 1){
			A.insert(x);
			set<int>::iterator it = A.lower_bound(x);
			set<int>::iterator cur = it;
			if(it != A.begin()){
				-- it;
//				std::cout<<"("<<*it<<" "<<x<<")"<<"\n"; 
				mn = std::min(mn,x - *it);
				for(int i = 0;i <= mn;++i)
				ans[i] = std::min(ans[i],1ll * (i + *it) ^ (i + x)),ans[i] = (i == 0) ? ans[i] : std::min(ans[i - 1],ans[i]);
			}
			it = ++cur;
			if(it != A.end()){
				mn = std::min(mn,*it - x);
//				std::cout<<"("<<x<<" "<<*it<<")"<<"\n"; 				
				for(int i = 0;i <= mn;++i)
				ans[i] = std::min(ans[i],1ll * (i + *it) ^ (i + x)),ans[i] = (i == 0) ? ans[i] : std::min(ans[i - 1],ans[i]);				
			} 
//			std::cout<<mn<<"\n";
		}else{
			if(x > mn)
			std::cout<<mn<<"\n";
			else
			std::cout<<ans[x]<<"\n"; 
		}
	}
}

结论三:考虑到答案的最终贡献方式,一定是让所有相邻对的某一位的后\(k\)位归\(0\),即可贡献维护的答案\(x\)不超过\(Vn\)个,考虑直接对这\(Vn\)个维护即可。

点击查看代码
ans[i] = std::min(ans[i],1ll * (i + *it) ^ (i + x)),ans[i] = (i == 0) ? ans[i] : std::min(ans[i - 1],ans[i]);
			}
			it = ++cur;
			if(it != A.end()){
				mn = std::min(mn,*it - x);
//				std::cout<<"("<<x<<" "<<*it<<")"<<"\n"; 				
				for(int i = 0;i <= mn;++i)
				ans[i] = std::min(ans[i],1ll * (i + *it) ^ (i + x)),ans[i] = (i == 0) ? ans[i] : std::min(ans[i - 1],ans[i]);				
			} 
//			std::cout<<mn<<"\n";
		}else{
			if(x > mn)
			std::cout<<mn<<"\n";
			else
			std::cout<<ans[x]<<"\n"; 

C

考虑每次维护中间块\([OL,OR] = k\)这一块的最小生成树,然后左边的在中间\(k\)个点虚树上的边,后缀维护,还有右边整块的前缀。

然后考虑删除时直接暴力重构。

#6

给我心态打崩了。
坐牢,签到都没签上。

A

怎么开局一直想着枚举左端点。
考虑强制钦定最小值的位置,设\(l_i\)为左边比\(h_i\)的大的数,\(r_i\)为在右边比他大的数。
\(x = l_i + r_i + 1\)
\(i = l_i + 1\)
考虑其对于一个询问\(k\)的贡献实际上是
\(\min(i,x - i + 1,k,x - k + 1)\)

考虑对于所有\(k\)都维护一个这样的贡献,那么其实际上可以通过拆成系数和常数分开维护。

(借一下rsx的代码)

#6 A
#include <bits/stdc++.h>
const int MAXN = 1e5 + 10;
using std::cin;
using std::cout;
template <typename T>
inline T min(const T &x, const T &y) {
	return x < y ? x : y;
}
template <typename T>
struct Fenwick_Tree {
	int limit;
	T t[MAXN];
	void add(int x, T y) {
		for (; x <= limit; x += x & -x) {
			t[x] += y;
		}
		return;
	}
	T sum(int x) {
		T ret = 0;
		for (; x; x -= x & -x) {
			ret += t[x];
		}
		return ret;
	}
	void build(int _N) {
		limit = _N;
	}
};
Fenwick_Tree <int> cyc;
int N, L, R, A[MAXN], Pos[MAXN];
long long K[MAXN], B[MAXN];
void add(long long *qjy, int l, int r, long long v) {
	qjy[l] += v;
	qjy[r + 1] -= v;
}
int main() {
	std::ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> N >> L >> R;
	for (int i = 1; i <= N; ++i) {
		cin >> A[i];
		Pos[A[i]] = i;
	}
	cyc.build(N);
	for (int i = 1; i <= N; ++i) {
		cyc.add(Pos[N - i + 1], 1);
		int Tk = cyc.sum(Pos[N - i + 1]);
		int Tmp = min(Tk, i - Tk + 1);
		add(K, 1, Tmp, 1);
		add(B, Tmp + 1, i - Tmp, Tmp);
		add(K, i - Tmp + 1, i, -1);
		add(B, i - Tmp + 1, i, i + 1);
	}
	for (int i = 1; i <= N; ++i) {
		K[i] += K[i - 1];
		B[i] += B[i - 1];
	}
	long long SUM = 0;
	for (int i = L; i <= R; ++i) {
		SUM ^= K[i] * i + B[i];
	}
	cout << SUM << '\n';
	return 0;
}

B

考虑对格子上的史莱姆进行\(dp\),这样发现其可以发现史莱姆合并的操作不影响答案。

则有\(X_{i,j} = \frac{1}{4} * (\sum_{(x,y)\ is\ next\ to\ (i,j) X_{x,y}}) + T_{x,y}\)

于是可以直接高斯消元得到\(40\)分。

#6 B 40分
#include<bits/stdc++.h>
#define ll long long 
#define N 105
#define mod 998244353
#define I4 748683265
#define pii std::pair<int,int>
#define mp std::make_pair


int dx[] = {-1,0,1,-0};
int dy[] = {0,-1,0,1};

int n,X[N],Y[N],a[N][N],T[N],ans;

#define mpi std::map<pii,int>

mpi P,C;

inline ll qpow(ll a,ll b){
	ll res = 1;
	while(b){
		if(b & 1)res = res * a % mod;
		a = a * a % mod;
		b >>= 1; 
	}
	return res;
}

inline void guass(int n){	
	for(int i = 1;i <= n;++i){
		int pos = i;
		for(int j = i;j <= n;++j)
		if(a[j][i]){
			pos = j;
			break;
		}
		if(pos ^ i)std::swap(a[i],a[pos]);
		int w = qpow(a[i][i],mod - 2);
		for(int j = 1;j <= n;++j){
			if(j == i)continue;
			int t = 1ll * a[j][i] * w % mod;
			for(int k = i;k <= n + 1;++k)
			a[j][k] = ((a[j][k] - 1ll * t * a[i][k] % mod) % mod + mod) % mod;
		}		
	}	
	for(int i = 1;i <= n;++i)
	a[i][n + 1] = 1ll * (1ll * a[i][n + 1] * (mod - 1) % mod * qpow(a[i][i],mod - 2)) % mod;
}

inline void solve(){
	for(int i = 1;i <= n;++i){
//		std::cout<<"DEL "<<i<<std::endl;
		for(int j = 0;j < 4;++j){
			int x = X[i] + dx[j],y = Y[i] + dy[j];
//			std::cout<<x<<" "<<y<<"\n";
			if(P.find(mp(x,y)) == P.end())continue;
			a[i][P[mp(x,y)]] = (I4) % mod; 
		}
		a[i][i] = mod - 1;
		a[i][n + 1] = T[i];
	}
	guass(n);
	for(auto i : C){
		int sx = i.first.first,sy = i.first.second,res = 0;
//		std::cout<<sx<<" "<<sy<<std::endl; 
		for(int j = 0;j < 4;++j){
			int nx = sx + dx[j];
			int ny = sy + dy[j];
			if(P.find(mp(nx,ny)) == P.end())continue;
			res = (res + a[P[mp(nx,ny)]][n + 1]) % mod;
		}
	ans = ans ^ (1ll * I4 * res % mod);
	}
	std::cout<<ans<<"\n";
}

int main(){
	scanf("%d",&n);
	for(int i = 1;i <= n;++i){
		scanf("%d%d%d",&X[i],&Y[i],&T[i]);
		P[mp(X[i],Y[i])] = i;
	}
	for(int i = 1;i <= n;++i)
	for(int j = 0;j < 4;++j){
		int x = X[i] + dx[j],y = Y[i] + dy[j];
		if(P.find(mp(x,y)) == P.end())C[mp(x,y)] = 1;
	}
	solve();
}


\(n \leq 2000\)时,考虑当我们每一列的元素只在\([i - m,i + m]\)中有值可以稀疏矩阵消元。

但是我发现有份代码直接对矩阵随机打乱然后记录有元素的值,跑的也很快。

#6 B 60分
inline void guass(int n){
	std::random_shuffle(a+1,a+n+1);
	for(R int i=1;i<=n;++i)
		for(R int j=1;j<=n+1;++j) if(a[i][j]) mw[i].insert(j);
	for(R int i=1;i<=n;++i){
		R int j=i;
		for(;j<=n;++j) if(a[j][i]) break;
		if(j^i) std::swap(a[i],a[j]),std::swap(mw[i],mw[j]);
		R int ha=qpow(a[i][i],P-2);
		for(j=1;j<=n;++j){
			if(j==i) continue;
			if(mw[j].find(i)==mw[j].end())continue;
			R int qc=1LL*(P-a[j][i])*ha%P;
			for(auto k:mw[i]){
				if(!a[j][k]) mw[j].insert(k);
				a[j][k]=(a[j][k]+1LL*qc*a[i][k])%P;
				if(!a[j][k]) mw[j].erase(k);
			}
		}
	}
	for(R int i=1;i<=n;++i){
		a[i][n+1]=(a[i][n+1]*(P-1LL)%P*qpow(a[i][i],P-2))%P;
	}
}

考虑矩阵情况,可以直接手动消元把除第一列的元素都用第一列表示,最后使用最后一列的关系对其消元,这样只有\(O(\sqrt n)\)个元,其复杂度为\(O(n^\frac{3}{2})\)

#7

A

考虑一次操作二会改变奇偶性。
一定是先操作二一次,然后再检查子区间是否有全偶区间即可。
当带0时,其把序列划分位若干子区间。
考虑扫描线即可。
找到前面第一个不是偶数的子区间即可。

image

B

\(xpp\)原本想考的是随机下最大上升子序列只有根号。

但是随机太好了。

被某些根号算法草爆了。

这里提供一个线段树覆盖代替原本的暴力并查集的做法。

点击查看代码
#include<bits/stdc++.h>
#define ll long long 
#define N 15005

int n,m,q,mx,a[N];

int sum[35000];

#define mid ((l + r) >> 1)
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)

using std::bitset;
using std::array;
using std::vector;

bitset<35000>tag;

vector<array<int,4>>M;

inline void cov(int u,int l,int r,int tl,int tr){
	if(tag[u])return ;
	if(tl <= l && r <= tr){tag[u] = 1,sum[u] = r - l + 1;return ;}
	if(tl <= mid)
	cov(ls(u),l,mid,tl,tr);
	if(tr > mid)
	cov(rs(u),mid + 1,r,tl,tr);
	tag[u] = tag[ls(u)] & tag[rs(u)],sum[u] = sum[ls(u)] + sum[rs(u)];
}

int main(){
	scanf("%d%d%d",&n,&m,&q);
	for(int i = 1;i <= n;++i){
		scanf("%d",&a[i]);
		M.push_back({i,i,a[i],0}); 
	}
	for(int i = 1;i <= m;++i){
		int l,r,x;
		scanf("%d%d%d",&l,&r,&x);
		M.push_back({l,r,x,i});
	}
	std::sort(M.begin(), M.end(), [&](array<int, 4> x, array<int, 4> y) { return x[2] < y[2]; });
	while(q -- ){
		int l1,r1,l2,r2;
		scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
		memset(sum,0,sizeof(sum)),tag.reset();
		ll ans = 0;
		int lst = 0;
		for (auto &p : M) if (!p[3] || p[3] >= l1 && p[3] <= r1) {
			int l = std::max(l2, p[0]), r = std::min(r2, p[1]);
			if (l <= r) cov(1, 1, n, l, r);
			ans += 1ll * p[2] * (sum[1] - lst);
			if ((lst = sum[1]) == r2 - l2 + 1) break;
		}
		printf("%lld\n", ans);		
	}	
}

#8

A

考虑 \((s_i,s_{i + 1},t_i,t_{i + 1})\) 一定有两个相同,所以最长公共子序列长度至少为 \(n\).

考虑限制很紧,一不小心就会大于 \(n\)

考虑只有两种情况:

\(ABABABAB\ \ C*C*C*C\)

\(ACACBCBC\ \ BCBCACAC\)

对着检查即可。

点击查看代码
//晦暗的宇宙,我们找不到光,看不见尽头,但我们永远都不会被黑色打倒。——Quinn葵因
#include<bits/stdc++.h>
#define ll long long
#define N 2000005
#define mod 998244353

char s[N],t[N];

int n;

int T;

bool pre[N],suf[N];

bool check(char a,char b){
	return a == '?' || a == b;
}

ll ans = 0;

inline void calc(char a,char b,char c){
	int now = 1;
	for(int i = 1;i <= (n << 1) + 1;++i)
	if(i & 1)
	now = now * check(s[i],a) * check(t[i],c) % mod;
	else
	now = now * check(s[i],b) * (check(t[i],a) + check(t[i],b)) % mod;
	ans = (ans + now) % mod;
	now = 1;
	for(int i = 1;i <= (n << 1) + 1;++i)
	if(i & 1)
	now = now * check(t[i],a) * check(s[i],c) % mod;
	else
	now = now * check(t[i],b) * (check(s[i],a) + check(s[i],b)) % mod;
	ans = (ans + now) % mod;
	pre[0] = suf[2 * n + 2] = 1;
	for(int i = 1;i <= (n << 1) + 1;++i){
		if(i & 1)
		pre[i] = pre[i - 1] & check(s[i],a) & check(t[i],b);
		else
		pre[i] = pre[i - 1] & check(s[i],c) & check(t[i],c);
	}	
	for(int i =(n << 1) + 1;i >= 1;--i){
		if(i & 1)
		suf[i] = suf[i + 1] & check(s[i],b) & check(t[i],a);
		else
		suf[i] = suf[i + 1] & check(s[i],c) & check(t[i],c);
	}		
	for(int i = 1;i <= (n << 1) + 1;i += 2)
	ans += (i == ((n << 1) + 1)) ? - (pre[i] & suf[i + 1]) : pre[i] & suf[i + 1] ;
} 

int main(){
	scanf("%d",&T);
	while(T -- ){
		ans = 0; 
		scanf("%d%s%s",&n,s + 1,t + 1);
      	calc('A','B','C');
        calc('A','C','B');
        calc('B','A','C');
        calc('B','C','A');
        calc('C','A','B');
        calc('C','B','A');	
		std::cout<<ans<<"\n";
	}
}
/*
2
?????
?????
*/ 

B

考虑树上枚举 \((x,y),LCA z\)

考虑当\(z < x < y\)或者 \(x < y < z\) 相当于给定了一个矩形,其矩形内的点均无效。

那么直接对询问扫描线即可。

考虑只要枚举相邻的\(x,y\),那么直接树上启发式合并即可。

#10

C

先考虑菊花的做法,发现其等价于给定点权,一条边的代价为 \(abs(a_x + a_y)\)

其可以使用 \(Brovka\) 解决。

考虑改为任意树,可以使用点/边分治,将路径改为上述形式解决。

每一轮,每点均找到异子树内的最小权边,共有 \(nlog\) 条边。

使用边分治更好处理。

点击查看代码
//晦暗的宇宙,我们找不到光,看不见尽头,但我们永远都不会被黑色打倒。——Quinn葵因
#include<bits/stdc++.h>
#define ll long long
#define N (int)2e6

using std::vector;
using std::pair;

#define pii pair<int,int>
#define mp std::make_pair

int head[N];

struct P{
	int to,next,w;
}E[N * 20];

int cnt = 1;

inline void add(int u,int v,int w){
	E[++cnt].to = v;
	E[cnt].next = head[u];
	E[cnt].w = w;
	head[u] = cnt;
}

inline void adde(int u,int v,int w){
	add(u,v,w);
	add(v,u,w);
}

vector<pii>G[N];

int m;

inline void reb(int u,int fa){
	int lst = 0;
	for(auto vi : G[u]){
		int v = vi.first;
		int w = vi.second;
		if(v == fa)continue;
		if(!lst){
			adde(u,v,w);
			lst = u;
		}else{
			adde(lst,++m,0);
			adde(m,v,w);
			lst = m;
		}
		reb(v,u);
	}
}

int siz[N],re,minn,sum;

int vis[N];

inline void getroot(int u,int fa){
	siz[u] = 1;
	for(int i = head[u];i;i = E[i].next){
		int v = E[i].to;
		if(v == fa || vis[i])continue;
		getroot(v,u);
		siz[u] += siz[v];
		if(std::max(siz[v],sum - siz[v]) < minn)
		minn = std::max(siz[v],sum - siz[v]),re = i;
	}
}

ll dis[N];

int ent;

struct nod {
	int u,v;
	ll w;
	nod(){}
	nod(int _u,int _v,ll _w){u = _u,v = _v,w = _w;}
}e[N * 40];

bool operator < (nod A,nod B){
	return A.w < B.w;
}

int fa[N];

inline int find(int x){return (x == fa[x]) ? x : fa[x] = find(fa[x]);}

pair<ll,int>Vec[3][N];

int sig[3];

inline void calc(int u,int tag,int fa){
	Vec[tag][++sig[tag]] = mp(dis[u],u);
	for(int i = head[u];i;i = E[i].next){
		int v = E[i].to;
		int w = E[i].w;
		if(v == fa || vis[i])continue;
		dis[v] = dis[u] + w;
		calc(v,tag,u);
	}
}

inline ll llabs(ll x){return x > 0 ? x : -x;}

inline void solve(int u){
	if(sum == 1)return;
	minn = 1e9;getroot(u,0);
	vis[re] = vis[re ^ 1] = 1;
//	std::cout<<u<<" "<<sum<<" "<<re<<"\n";
	int x = E[re].to,y = E[re ^ 1].to;
	sig[1] = sig[2] = 0;
	dis[x] = E[re].w;calc(x,1,0);
	dis[y] = 0;calc(y,2,0);
	std::sort(Vec[1] + 1,Vec[1] + sig[1] + 1);
	std::sort(Vec[2] + 1,Vec[2] + sig[2] + 1);
	for(int i=1,j=sig[2];i<=sig[1];i++) {
		while(j&&Vec[2][j].first>-Vec[1][i].first) j--;
		if(j+1<=sig[2]) e[++ent]=nod(Vec[1][i].second,Vec[2][j+1].second,llabs(Vec[1][i].first+Vec[2][j+1].first));
		if(j) e[++ent]=nod(Vec[1][i].second,Vec[2][j].second,llabs(Vec[1][i].first+Vec[2][j].first));
	}
	std::swap(sig[1],sig[2]);
	for(int i=1;i<=std::max(sig[1],sig[2]);i++) swap(Vec[1][i],Vec[2][i]);
	for(int i=1,j=sig[2];i<=sig[1];i++) {
		while(j&&Vec[2][j].first>-Vec[1][i].first) j--;
		if(j+1<=sig[2]) e[++ent]=nod(Vec[1][i].second,Vec[2][j+1].second,llabs(Vec[1][i].first+Vec[2][j+1].first));
		if(j) e[++ent]=nod(Vec[1][i].second,Vec[2][j].second,llabs(Vec[1][i].first+Vec[2][j].first));
	}
	int now = sum - siz[x];
	sum = siz[x];
	solve(x);
	sum = now;
	solve(y);
}

int n;

int main(){
//	freopen("q.in","r",stdin);
//	freopen("q.ans","w",stdout);
	scanf("%d",&n);
	m = n;
	for(int i = 1;i < n;++i){
		int x,y,w;
		scanf("%d%d%d",&x,&y,&w);
		G[x].emplace_back(y,w);
		G[y].emplace_back(x,w);
	}
	reb(1,0);
	sum = m;
	solve(1);
	std::sort(e + 1,e + ent + 1);
	ll ans = 0;
	for(int i = 1;i <= m;++i)fa[i] = i;
	for(int i = 1;i <= ent;++i){
//		std::cout<<e[i].u<<" "<<e[i].v<<" "<<e[i].w<<"\n";
		if(find(e[i].u) ^ find(e[i].v)){
			fa[fa[e[i].u]] = fa[e[i].v];
			ans += e[i].w;
		}
	}
	std::cout<<ans<<"\n";
}
posted @ 2022-02-27 17:47  fhq_treap  阅读(124)  评论(0编辑  收藏  举报