#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
#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时,其把序列划分位若干子区间。
考虑扫描线即可。
找到前面第一个不是偶数的子区间即可。
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";
}