BZOJ 1977: [BeiJing2010组队]次小生成树(Kruskal+树上倍增)
1977: [BeiJing2010组队] 次小生成树 Tree
Time Limit: 10 Sec
Memory Limit: 512 MBDescription###
小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当>小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成
树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 >EM,严格次小生成树选择的边集是 ES,那么需要满足:
这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
Input###
第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z >表示,点 x 和点y之间有一条边,边的权值为z。。
Output###
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
Sample Input 1###
5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
Sample Output 1###
11
HINT###
数据中无向图无自环;
50% 的数据N≤2 000 M≤3 000;
80% 的数据N≤50 000 M≤100 000;
100% 的数据N>≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。
题目地址: BZOJ 1977: [BeiJing2010组队]次小生成树
题解:
先求出最小生成树,然后枚举每条不在该树上的边
求出此边两点的树上路径中小于此边长度的最大的边(题目要求严格次小)
可用树上倍增解决,因为最大的边可能等于枚举的边,所以还要求出严格次大的边
虽然很水但我还是被优先级和初值坑了三个小时QWQ
www.cnblogs.com/AGFghy/
AC代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
struct path
{
int x,y,l;
}p[300005];
int n,m,num,f1,f2;
ll now,sum,n1,n2,ans;
int point[200005],len[200005],Next[200005],head[100005],fa[100005],dep[100005],mark[100005],f[100005][25];
ll mx1[100005][25],mx2[100005][25];
bool cmp(path p1,path p2)
{
return p1.l<p2.l;
}
int find(int k)
{
if (k!=fa[k]) return fa[k]=find(fa[k]);
return k;
}
void add(int u,int v,int nl)
{
num++;
point[num]=v;
len[num]=nl;
Next[num]=head[u];
head[u]=num;
}
void find(int now,int pre)
{
dep[now]=dep[pre]+1;
f[now][0]=pre;
for (int i=head[now]; i!=-1; i=Next[i])
{
int v=point[i];
if (v!=pre)
{
mx1[v][0]=len[i];
find(v,now);
}
}
}
ll max(ll x,ll y)
{
if (x>y) return x;
return y;
}
ll min(ll x,ll y)
{
if (x<y) return x;
return y;
}
void lca(int u,int v)
{
if (dep[u]<dep[v]) swap(u,v);
int deep=dep[u]-dep[v];
for (int i=0; i<=20; i++)
if ((deep&(1<<i))>0)
{
n2=max(n2,mx2[u][i]);
if (n1!=mx1[u][i]) n2=max(n2,min(n1,mx1[u][i]));
n1=max(n1,mx1[u][i]);
u=f[u][i];
}
for (int i=20; i>=0; i--)
if (f[u][i]!=f[v][i])
{
n2=max(n2,mx2[u][i]);
if (n1!=mx1[u][i]) n2=max(n2,min(n1,mx1[u][i]));
n1=max(n1,mx1[u][i]);
u=f[u][i];
n2=max(n2,mx2[v][i]);
if (n1!=mx1[v][i]) n2=max(n2,min(n1,mx1[v][i]));
n1=max(n1,mx1[v][i]);
v=f[v][i];
}
if (u!=v)
{
n2=max(n2,mx2[u][0]);
if (n1!=mx1[u][0]) n2=max(n2,min(n1,mx1[u][0]));
n1=max(n1,mx1[u][0]);
n2=max(n2,mx2[v][0]);
if (n1!=mx1[v][0]) n2=max(n2,min(n1,mx1[v][0]));
n1=max(n1,mx1[v][0]);
}
return;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1; i<=n; i++)
head[i]=-1;
for (int i=1; i<=m; i++)
scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].l);
sort(p+1,p+m+1,cmp);
for (int i=1; i<=n; i++)
fa[i]=i;
now=n-1;
for (int i=1; i<=m; i++)
{
f1=find(p[i].x);
f2=find(p[i].y);
if (f1!=f2)
{
now--;
fa[f2]=f1;
sum+=p[i].l;
mark[i]=1;
add(p[i].x,p[i].y,p[i].l);
add(p[i].y,p[i].x,p[i].l);
if (now==0) break;
}
}
memset(mx1,-1,sizeof(mx1));
memset(mx2,-1,sizeof(mx2));
find(1,0);
for (int j=1; j<=20; j++)
for (int i=1; i<=n; i++)
if (f[i][j-1])
{
f[i][j]=f[f[i][j-1]][j-1];
mx1[i][j]=max(mx1[i][j-1],mx1[f[i][j-1]][j-1]);
if (mx1[i][j-1]!=mx1[f[i][j-1]][j-1]) mx2[i][j]=min(mx1[i][j-1],mx1[f[i][j-1]][j-1]);
mx2[i][j]=max(mx2[i][j],max(mx2[i][j-1],mx2[f[i][j-1]][j-1]));
}
ans=1ll<<50;
for (int i=1; i<=m; i++)
if (mark[i]==0)
{
n1=-1; n2=-1;
lca(p[i].x,p[i].y);
if (n1!=p[i].l)
{
now=sum+p[i].l-n1;
ans=min(ans,now);
}
else if (n2!=-1)
{
now=sum+p[i].l-n2;
ans=min(ans,now);
}
}
printf("%lld\n",ans);
return 0;
}