倍增
点击查看代码
//最大值不支持减法操作
//倍增代码,求区间的最大值
#include <bits/stdc++.h>
using namespace std;
int n,a[1000000],f[100000][20];//f的j次方开到20就可以达到1000000
int x[100010];//x[i]代表长度为i的区间,用两个长度为2^x[i]的区间能够覆盖
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
x[i]=0;
for(int i=2;i<=n;i++)
{
x[i]=x[i>>1]+1;//x[111]=x[55]+1;
}
cin>>m;
for(int i=1;i<=m;i++)
{
int l,r;
cin>>l>>r;
int len=r-l+1;
int j=x[len];
cout<<max(f[l][x[len]],f[r-(1<<x[len])+1][x[len]]);
}
for(int i=1;i<=n;i++)
{
f[i][0]=a[i];
}
for(int j=1;(1<<j)<=n;j++)
{
for(int i=1;i+(1<<j)-1<=n;i++)
{
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
}
return 0;
}
二分
点击查看代码
//二分代码
#include <bits/stdc++.h>
using namespace std;
int a[100010],n,m;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
sort(a+1,a+n+1);
cin>>m;
for(int i=1;i<=m;i++)
{
int x;
cin>>x;
int l=0,r=n;//代表当前x可能出现的范围是l+1~r
while(l+1!=r)
{
int m=(l+r)>>1;
if(x<=a[m])
{
r=m;//当前答案区间,从l+1~r,变为l+1~m
}
else
{
l=m;
}
}
if(a[r]==x) 出现过; else 没出现过;
}
return 0;
}
快速幂
点击查看代码
//快速幂代码
int ksm(int x,int y,int p)//计算x^y%p
{
if(y==0)
{
return 1;
}
int z=ksm(x,y/2,p);//z=x^(y/2)%p
z=1ll*z*z%p;
if(y&1)
{
z=1ll*z*x%p;
}
return z;
}
矩阵乘法
点击查看代码
//矩阵乘法
struct matrix
{
int n,m;//行数,列数
int n[5][5];//矩阵里每一个数是多少
matrix()
{
n=m=0;
memset(z,0,sizeof(a));
}
};
matrix operator*(const matrix &m1,const matrix &m2)//计算m1*m2
{
matrix m3;
m3.n=m1.n; m3.m=m2.m;
for(int i=1;i<=m3.n;i++)
{
for(int j=1;j<=m3.m;j++)
{
for(int k=1;k<=m1.m;k++)
{
m3.a[i][j]+=m1.a[i][k]*m2.a[k][j];
}
}
}
return m3;
}
调用:m3=m1*m2;
矩阵斐波那契数列
点击查看代码
//矩阵斐波那切数列
#include <iostream>
using names
struct matrix
{
int n,m;//行数,列数
int a[5][5];//矩阵里每一个数是多少
matrix()
{
n=m=0;
memset(z,0,sizeof(a));
}
};
matrix operator*(const matrix &m1,const matrix &m2)//计算m1*m2
{
matrix m3;
m3.n=m1.n; m3.m=m2.m;
for(int i=1;i<=m3.n;i++)
{
for(int j=1;j<=m3.m;j++)
{
for(int k=1;k<=m1.m;k++)
{
m3.a[i][j]+=m1.a[i][k]*m2.a[k][j];
}
}
}
return m3;
}
matrix ksm(matrix x,int y)//矩阵x^y=?
{
if(y==0)
{
matrix z;
z.n=z.m=x.n;
for(int i=1;i<=z.n;i++)
{
z.a[i][i]=1;
}
return z;
}
matrix z=ksm(x,y/2);
z=z*z;
if(y&1) z=z*x;
return z;
}
int main()
{
cin>>n;
matrix A;//[f0,f1]
A.n=1;A.m=2;
A.a[1][1]=0;A.a[1][2]=1;
matrix B;
A.n=1;A.m=2;
B.a[1][1]=0;B.a[1][2]=1;
B.a[2][1]=0;B.a[2][2]=1;
matrix C=A*ksm(B,n);//[fn,fn+1]
cout<<C.a[1][1]<<endl;
return 0;
}
bfs
点击查看代码
//bfs搜索
#include <iostream>
using namespace std;
int n,m,a[10010][10010],sx,sy,tx,ty;
int step[maxn][maxn];//step[i][j],代表从起点走到(i,j)需要多少步
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
int main()
{
cin>>n>>m;//行数,列数
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>a[i][j];//a[i][j]==1代表障碍物
}
}
cin>>sx>>sy;//起始坐标
cin>>tx>>ty;//终点坐标
memset(step,-1,sizeof(step));
step[sx][sy]=0;
queue<pair<int,int> > q;//用来存能够向外进行扩展step的点
q.push(make_pair(sx,sy));
while(q.size())
{
int x=q.front().first;
int y=q.front().second;
q.pop();//当前的点为(x,y)
for(int i=0;i<4;i++)//枚举上下左右四个方向
{
int xx=x+dx[i];
int yy=y+dy[i];//从(x,y)走到了(xx,yy)
if(xx>=1&&xx<=n&&yy>=1&&y<=m&&a[xx][yy]!=1&&step[xx][yy]==-1)
{
step[xx][yy]=step[x][y]+1;
q.push(make_pair(xx,yy));
}
step[xx][yy]=step[x][y]+1;
q.push(make_pair(xx,yy));
}
}
}
dfs
点击查看代码
//dfs搜索
#include <bits/stdc++.h>
using namespace std;
int n,m,a[100010];
int ans=-114514;
void dfs(int now,int nownum,int nowsum)//当前要看第now个元素选不选,已经选了nownum个数,已经选了的数的和是nowsum
{
if(now>n)
{
if(nownum==m) ans=max(ans,nowsum);
return;
}
dfs(now+1,nownum,nowsum);//不选
dfs(now+1,nownum+1,nowsum+a[now]);//选
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
dfs(1,0,0);
cout<<ans;
return 0;
}
dfs可行性剪枝优化
点击查看代码
//dfs可行性剪枝优化
#include <bits/stdc++.h>
using namespace std;
int n,m,a[100010];
int ans=-114514;
void dfs(int now,int nownum,int nowsum)//当前要看第now个元素选不选,已经选了nownum个数,已经选了的数的和是nowsum
{
if(nownum>m) return;//可行性剪枝
if(nownum+n-now+1<m) return;
if(now>n)
{
if(nownum==m) ans=max(ans,nowsum);
return;
}
dfs(now+1,nownum,nowsum);//不选
dfs(now+1,nownum+1,nowsum+a[now]);//选
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
dfs(1,0,0);
cout<<ans;
return 0;
}
dfs最优性剪枝优化
点击查看代码
//dfs最优性剪枝优化
#include <bits/stdc++.h>
using namespace std;
int n,m,a[100010];
int ans=-114514;
void dfs(int now,int nownum,int nowsum)//当前要看第now个元素选不选,已经选了nownum个数,已经选了的数的和是nowsum
{
if(nowsum+sum[n]-sum[now-1]<=ans) return;//最优性剪枝
if(now>n)
{
if(nownum==m) ans=max(ans,nowsum);
return;
}
dfs(now+1,nownum,nowsum);//不选
dfs(now+1,nownum+1,nowsum+a[now]);//选
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
dfs(1,0,0);
cout<<ans;
return 0;
}
用负数下标
点击查看代码
//用负数下标
#include<bits/stdc++.h>
using namespace std;
int n,ans,a[1000005];
int *b=a+500000;
signed main(){
b[-233];//指向 a[500000 - 233]
//b 的范围为 -500000 ~ 500000];
}
手写队列
点击查看代码
//手写队列
#include<bits/stdc++.h>
using namespace std;
struct shouxie_queue{
int z[1000005];
int head;//第一个人在什么位置
int tail;//最后一个人在什么位置
shouxie_queue(){//构造函数
head=1;
tail=0;
memset(z,0,sizeof(z));
//memset(z,1,sizeof(z))
//可以用的:-1 0x3f 0x3f3f3f3f(无穷大)
}
void push(int x){
z[++tail]=x;
}
void pop(){//在外面调用时判空
head++;
}
int front(){
return z[head];
}
int size(){
return tail-head+1;
}
};
双向bfs
点击查看代码
//双向bfs
#include<iostream>
using namespace std;
int a[4][4],b[4][4];
int step[87654321+1];//step[i] 代表走到状态i所需要的最小步数
int belong[87654321+1];
//belong[i]==0 代表这个状态还没有被搜到
//belong[i]==1 代表这个状态是从起点被搜到的
//belong[i]==2 代表这个状态是从终点被搜到的
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
bool vis[9];//vis[i]i这个数有没有出现过
int main()
{
int s=0;
for (int i=1;i<=3;i++)
for (int j=1;j<=3;j++)
{
cin >> a[i][j];
if (i!=3 || j!=3) s=s*10 + a[i][j];
}
memset(step,-1,sizeof(step));
step[s] = 0; belong[s] = 1;
step[12345678]=0; belong[12345678] = 2;
queue<int> q;
q.push(s);
q.push(12345678);
while (q.size())
{
int s=q.front();
int cur_step = step[s];
int cur_belong = belong[s];
q.pop();
int x,y;
memset(vis,false,sizeof(vis));
for (int i=3;i>=1;i--)
for (int j=3;j>=1;j--)
if (i!=3 || j!=3)
{
a[i][j] = s%10;
s=s/10;
vis[a[i][j]] = true;
}
for (int i=0;i<9;i++)
if (!vis[i]) a[3][3] = i;
for (int i=1;i<=3;i++)
for (int j=1;j<=3;j++)
if (a[i][j]==0) x=i,y=j;
for (int d=0;d<4;d++)
{
int xx=x+dx[d];
int yy=y+dy[d];//(x,y) 和 (xx,yy)交换
if (xx>=1 && xx<=3 && yy>=1 && yy<=3)
{
swap(a[x][y],a[xx][yy]);
s=0;
for (int i=1;i<=3;i++)
for (int j=1;j<=3;j++)
if (i!=3 || j!=3) s=s*10+a[i][j];
if (step[s] == -1)
{
step[s] = cur_step+1;
belong[s] = cur_belong;
q.push(s);
}
else
{
if (belong[s] != cur_belong)
{
ans = cur_step + 1 + step[s];
cout << ans << endl;
exit(0);
}
}
swap(a[x][y],a[xx][yy]);
}
}
}
cout << step[12345678] << endl;
}
**单源最短路算法Dijkstra**
<details>
<summary>点击查看代码</summary>
//单源最短路算法Dijkstra
//选一个最短路已经求好的点,选的点一定是当前 dist 最小的点。
进行松弛操作(用自己的最短路去更新自己周围的最短路)
include<bits/stdc++.h>
using namespace std;
vector<pair<int,int> > z[100005];
int dist[100005];//dist[i] 代表从起点到 i 的最短路bool vis[i];//vis[i]代表 i 的最短路有没有被求出来
void add_edge(int s,int e,int d){
z[s].push_back(make_pair(e,d));
}
int n,m;
void Dijkstra(int s){//以 s 作为起点算最短路
memset(dist,0x3f,sizeof(dist));
dist[s]=0;
for(int i=1;i<=n;i++){
//选一个 dist 最小的点
int p=0;
for(int j=1;j<=n;j++)
if(!vis[j]&&(p==0||dist[j]<dist[p]))p=j;
vis[p]=true;
//用这个点去进行松弛操作
for(int j=0;j<z[p].size();j++){
int q=z[p][j].first;
int d=z[p][j].second;///这是一条从 p 到 q 长度为 d 的边
dist[q]=min(dist[q],dist[p]+d);
}
}
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
int s,e,d;
cin>>s>>e>>d;
add_edge(s,e,d);
}
Dijkstra(1);
int T;
cin>>T;
while(T--){
int x;
cin>>x;
cout<<dist[x]<<'\n';
}
return 0;
}
</details>
**flogd押维**
<details>
<summary>点击查看代码</summary>
//flogd押维 时间复杂度:O(n^3)
include<bits/stdc++.h>
using namespace std;
int dist[1005][1005];//dist[i][j][k] 代表从 j 走到 k 使得中间经过的节点编号 <=i 的情况下的最短路
const int INF=0x3f3f3f3f;
int n,m;//n 点 m 边
signed main(){
memset(dist,0x3f,sizeof(dist));//把数组每一个元素赋值为INF
cin>>n>>m;
for(int i=1;i<=m;i++){
int s,e,d;
cin>>s>>e>>d;//一条从 s 到 e 长度为 d 的边
dist[s][e]=min(dist[s][e],d);
}
for(int i=1;i<=n;i++)
dist[i][i]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
dist[j][k]=min(dist[j][k],dist[j][i]+dist[i][k]);
int T;
cin>>T;
while(T--){
int i,j;
cin>>i>>j;
cout<<dist[i][j]<<'\n';
}
return 0;
}
</details>
**多源最短路算法flogd的代码:原始版本**
<details>
<summary>点击查看代码</summary>
//多源最短路算法flogd的代码:原始版本 时间复杂度是:O(n^3)
include<bits/stdc++.h>using namespace std;
int dist[1005][1005][1005];//dist[i][j][k] 代表从 j 走到 k 使得中间经过的节点编号 <=i 的情况下的最短路
const int INF=0x3f3f3f3f;
int n,m;//n 点 m 边
signed main(){
memset(dist,0x3f,sizeof(dist));//把数组每一个元素赋值为INF
cin>>n>>m;
for(int i=1;i<=m;i++){
int s,e,d;
cin>>s>>e>>d;//一条从 s 到 e 长度为 d 的边
dist[0][s][e]=min(dist[0][s][e],d);
}
for(int i=1;i<=n;i++)
dist[0][i][i]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++) dist[i][j][k]=min(dist[i-1][j][k],dist[i-1][j][i]+dist[i-1][i][k]);
int T;
cin>>T;
while(T--){
int i,j;
cin>>i>>j;
cout<<dist[n][i][j]<<'\n';
}
return 0;
}
</details>
**存图代码:编表**
<details>
<summary>点击查看代码</summary>
//存图代码:编表 优点是省内存,缺点是慢
include <bits/stdc++.h>
using namespace std;
int n,m,z[100010];
vector< pair<int,int> >z[maxn];//z[i]:代表所有以i作为起点的边
//z[i][j]:代表以i为起点的第j条边
//z[i][j].first:代表以i为起点的第j条边的终点
//z[i][j].second:代表以i为起点的第j条边的长度
void add_edge(int s,int e,int d)//添加一条从s出发 到e 长度为d的边
{
z[s].push_back(make_pair(e,d));
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int s,e,d;//起点s,终点e,长度d
cin>>s>>e>>d;
add_edge(s,e,d);
add_edge(e,s,d);
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<z[i].size();j++)
{
int e=z[i][j].first;
int d=z[i][j].second;
}
}
return 0;
}
</details>
**存图代码:邻接矩阵**
<details>
<summary>点击查看代码</summary>
//存图代码:邻接矩阵 缺点是消耗空间大,优点是简单,快
include <bits/stdc++.h>
using namespace std;
int z[101][101];//z[i][j]代表从i到j那条边的长度
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int s,e,d;
cin>>s>>e>>d;//起点为s,终点为e,长度为d的边
z[s][e]=d;
//如果要存无向图加上z[e][s]=d;
}
return 0;
}
</details>
**拓扑排序**
<details>
<summary>点击查看代码</summary>
染色法:把一个点染色为 1,然后一层一层向外染色。如果一条边连接的两个点的颜色一样,就说明不是二分图,否则就是。
拓扑排序:针对 DAG。
对有向图中的每一个点进行排序,使得最后的排序结果都是从左向右指的。
不断删除入度为0的点,然后不断把入度为0的点加入答案。
那如何删除一个点呢?其实不需要删除,只需要将入度减掉即可。#include<bits/stdc++.h>using namespace std;
vector<pair<int,int>> z[100005];//z[i]代表所有以 i 为起点的边//z[i][j] 代表以 i 为起点的第 j 条边//z[i][j].first 代表以 i 为起点的第 j 条边的终点 //z[i][j].second 代表以 i 为起点的第 j 条边的长度
void add_edge(int s,int e,int d){//添加一个从 s 出发到 e 的长度为 d 的边
//push_back:向 vector 的末尾加入一个元素
z[s].push_back(make_pair(e,d));
}
int n,m;
int in[100005];//in[i] 代表 i 点的入度
int result[100005];
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
int s,e,d;
cin>>s>>e>>d;//起点为s,终点为e,长度为d
add_edge(s,e,d);
in[e]++;
}
int cnt=0;
for(int i=1;i<=n;i++)
if(in[i]==0)result[++cnt]=i;
for(int i=1;i<=n;i++){//当前要取出拓扑排序的结果中的第 i 个点
int p=result[i];
for(int j=0;j<z[p].size();j++){
int q=z[p][j];//p->q的边
in[q]--;
if(in[q]==0)result[++cnt]=q;
}
}
return 0;
}
</details>
**Dijkstra优化**
<details>
<summary>点击查看代码</summary>
//Dijkstra优化用堆
include<bits/stdc++.h>
using namespace std;
vector<pair<int,int>> z[100005];
int dist[100005];//dist[i] 代表从起点到 i 的最短路
bool vis[i];//vis[i]代表 i 的最短路有没有被求出来
void add_edge(int s,int e,int d){
z[s].push_back(make_pair(e,d));
}
int n,m;
void Dijkstra(int s){//以 s 作为起点算最短路
memset(dist,0x3f,sizeof(dist));
dist[s]=0;
priority_queue<pair<int,int> > heap;
//first 用来存距离的相反数
//second 用来存点的编号
for(int i=1;i<=n;i++)
heap.push(make_pair(-dist[i],i));
for(int i=1;i<=n;i++){
//选一个 dist 最小的点
while(vis[heap.top().second])
heap.pop();
int p=heap.top().second;
heap.pop();
vis[p]=true;
//用这个点去进行松弛操作
for(int j=0;j<z[p].size();j++){
int q=z[p][j].first;
int d=z[p][j].second;///这是一条从 p 到 q 长度为 d 的边
if(dist[q]>dist[p]+d){
dist[q]=dist[p+d];
heap.push(make_pair(-dist[q],q));
}
}
}
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
int s,e,d;
cin>>s>>e>>d;
add_edge(s,e,d);
}
Dijkstra(1);
int T;
cin>>T;
while(T--){
int x;
cin>>x;
cout<<dist[x]<<'\n';
}
return 0;
</details>
**Bellman_ford**
<details>
<summary>点击查看代码</summary>
//可以针对负数边权,但是更慢。
include<bits/stdc++.h>
using namespace std;
int s[100005],d[1000005],e[1000005];
int n,m;
signed main(){
cin>>n>>m;
for(int i=1;i<=m;i++)
cin>>s[i]>>e[i]>>d[i];
memset(dist,0x3f,sizeof(dist));
dist[1]=0;
for(int i=1;i<n;i++)
for(int j=1;j<=m;j++)
dist[e[j]]=min(dist[e[j]],dist[s[j]]+d[j]);
}
</details>
**SPFA**
<details>
<summary>点击查看代码</summary>
//维护能改变其他点最短路的点的队列,直至队列为空。
include<bits/stdc++.h>
using namespace std;
vector<pair<int,int> > z[500005];
int dist[500005];//dist[i] 代表从起点到 i 的最短路
bool vis[500005];//vis[i]代表 i 在不在队列里
void add_edge(int s,int e,int d){
z[s].push_back(make_pair(e,d));
}
int n,m,x;
void SPFA(int s){//以 s 作为起点算最短路
memset(dist,0x3f,sizeof(dist));
dist[s]=0;
queue
q.push(s);
vis[s]=true;
while(!q.empty()){
int p=q.front();
q.pop();
vis[p]=false;
for(int i=0;i<z[p].size();i++){
int e=z[p][i].first;
int d=z[p][i].second;
if(dist[e]>dist[p]+d){
dist[e]=dist[p]+d;
if(!vis[e])q.push(e),vis[e]=true;
}
}
}
}
signed main(){
cin>>n>>m>>x;
for(int i=1;i<=m;i++){
int s,e,d;
cin>>s>>e>>d;
add_edge(s,e,d);
}
SPFA(x);
for(int i=1;i<=n;i++){
cout<<dist[i]<<' ';
}
return 0;
}
</details>
次小生成树:第二小的生成树。
次小生成树:删掉一条边,再加上一条边,使得差值尽量小,并且要是一个树。
次小生成树:如果一条边在最小生成树上,我们就叫他树边,如果不在最小生成树上就叫他非树边。
次小生成树:删掉一条树边,加上一条非树边。
次小生成树:倍增 LCA 询问环上最大的值(章鱼图)。
最小生成树:从一张 m 条边的图中找 n−1 条边,使得找出来的边和已有的点构成一棵树,组成的图就叫做原图的生成树。一个生成树的大小是选出来的所有边的边权之和。大小最小的生成树被称为 最小生成树。
**最小生成树基础算法**
<details>
<summary>点击查看代码</summary>
//O(nm)
//把所有的边按照他们的边权进行排序。
怎么知道有没有环呢?并查集,查看他们最后指向的点是否一样。
include<bits/stdc++.h>
using namespace std;
int to[MAXN];//to[i] 表示 i 点在并查集里面的箭头指向谁
int go(int p){//看一下点 p 沿着并查集箭头最后会走到哪里
if(to[p]==p)return p;//指向自己
else return go(to[p]);//递归调用
}
struct edge{
int s,e,d;
}ed[MAXN];//ed[i] 代表第 i 条边是在 s 与 e 之间的长度为 d 的边
int n,m;
bool cmp(edge a,edge b){
return a.d<b.d;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=m;i++)
cin>>ed[i].s>>ed[i].e>>ed[i].d;
sort(ed+1,ed+m+1,cmp);//所有边按照边权进行排序
for(int i=1;i<=n;i++)
to[i]=i;//初始化并查集
int ans=0;//生成树大小
int cnt=0;//边的数量
for(int i=1;i<=m;i++){
if(cnt==n-1)break;
int p1=ed[i].s,p2=ed[i].e,d=ed[i].d;
if(go(p1)!=go(p2)){//不在同一个连通块
ans+=d;
to[go(p1)]=go(p2);
++cnt;
}
}
cout<<ans<<endl;
}
</details>
**最小生成树并查集的路径压缩**
<details>
<summary>点击查看代码</summary>
//O(mlogm) sort最慢
include<bits/stdc++.h>
using namespace std;
int to[MAXN];//to[i] 表示 i 点在并查集里面的箭头指向谁
int go(int p){//看一下点 p 沿着并查集箭头最后会走到哪里
//O(1)
if(to[p]==p)return p;//指向自己
/else{
int q=go(to[p]);
to[p]=q;
return q;
}/
else return to[p]=go(to[p]);
}
struct edge{
int s,e,d;
}ed[MAXN];//ed[i] 代表第 i 条边是在 s 与 e 之间的长度为 d 的边
int n,m;
bool cmp(edge a,edge b){
return a.d<b.d;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=m;i++)
cin>>ed[i].s>>ed[i].e>>ed[i].d;
sort(ed+1,ed+m+1,cmp);//所有边按照边权进行排序
for(int i=1;i<=n;i++)
to[i]=i;//初始化并查集
int ans=0;//生成树大小
int cnt=0;//边的数量
for(int i=1;i<=m;i++){//O(m)
if(cnt==n-1)break;
int p1=ed[i].s,p2=ed[i].e,d=ed[i].d;
if(go(p1)!=go(p2)){//不在同一个连通块
ans+=d;
to[go(p1)]=go(p2);
++cnt;
}
}
cout<<ans<<endl;
}
</details>