这个题还是挺有意思的...
一个小结论是:在一个$n$点$m$边的图中,如果度数最小的点度数为$d$,那么$d^2=O(m)$,因为$d\leq\frac{2m}n$,所以$d^2\leq dn\leq2m$
把链按权值从小到大排序,对一条链$(u,v)$,设在它上面的限制数量为$k$,如果$k\lt\text{dis}(u,v)$,那么我们可以让这条链上的所有点连通,直接用并查集把链上的点暴力合并即可
否则我们想要在$O(k)$的时间内计算答案,这样这部分的总复杂度就是$O(p)$的
先把限制建成图,找到度数最小的那个点$x$,不与$x$相邻的那些点(设此集合再加上$x$为$X$)都可以和它合并,现在考虑那些与$x$相邻的点(设此集合为$Y$),只有两种可能的连边:$X\rightarrow Y$,$Y\rightarrow Y$
对于$X\rightarrow Y$,我们枚举$y\in Y$,如果$d_y\lt\left\lvert X\right\rvert$,那么它一定可以和$X$中某点连边,否则$O(d_y)$打标记再遍历所有$X$中节点即可,这部分复杂度是$O\left(\sum d_y\right)=O(k)$
对于$Y\rightarrow Y$,直接$O\left(d_x^2\right)$枚举即可,由开始的那个结论得这部分的复杂度也是$O(k)$
最后一个小问题是:要维护一个支持链并和两点并的并查集,这里维护两个并查集:链并的和实际集合的,在链并操作的同时更新实际集合的并查集即可
总复杂度$O(n+m\log m+q\alpha(n))$,跑得非常快
#include<stdio.h>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int inf=2147483647;
int fa[300010],dep[300010],d[300010],f[300010],ot[300010],N,M;
bool vis[300010];
ll ans;
struct ch{
int x,y,w,i;
}p[300010];
bool operator<(ch a,ch b){return a.w<b.w;}
struct edge{
int x,y;
edge(int x=0,int y=0):x(x),y(y){}
};
vector<edge>e[300010];
vector<edge>::iterator it;
vector<int>g[300010];
vector<int>::iterator i1,i2;
struct dsu{
int fa[300010];
int get(int x){return x==fa[x]?x:(fa[x]=get(fa[x]));}
}c,b;
void merge(int x,int y,int w){
x=b.get(x);
y=b.get(y);
if(x!=y){
ans+=w;
b.fa[x]=y;
}
}
bool check(int x,int y,int k){
while(k--){
if(dep[x]<dep[y])swap(x,y);
x=fa[x];
if(x==y)return 0;
}
return 1;
}
void get(int x,int y){
N=0;
while(x!=y){
if(dep[x]<dep[y])swap(x,y);
f[++N]=x;
x=fa[x];
}
f[++N]=x;
}
int main(){
int n,m,q,i,j,x,y,mn;
scanf("%d%d%d",&n,&m,&q);
for(i=2;i<=n;i++)scanf("%d",fa+i);
for(i=1;i<=n;i++)dep[i]=dep[fa[i]]+1;
for(i=1;i<=m;i++){
scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].w);
p[i].i=i;
}
while(q--){
scanf("%d%d%d",&i,&x,&y);
e[i].push_back(edge(x,y));
}
sort(p+1,p+m+1);
for(i=1;i<=n;i++)c.fa[i]=b.fa[i]=i;
for(i=1;i<=m;i++){
#define merge(x,y) merge(x,y,p[i].w)
vector<edge>&v=e[p[i].i];
if(check(p[i].x,p[i].y,v.size())){
x=c.get(p[i].x);
y=c.get(p[i].y);
while(x!=y){
if(dep[x]<dep[y])swap(x,y);
merge(x,fa[x]);
c.fa[x]=fa[x];
x=c.get(x);
}
}else{
for(it=v.begin();it!=v.end();it++){
x=it->x;
y=it->y;
d[x]++;
d[y]++;
g[x].push_back(y);
g[y].push_back(x);
}
get(p[i].x,p[i].y);
x=0;
mn=inf;
for(j=1;j<=N;j++){
if(d[f[j]]<mn){
mn=d[f[j]];
x=f[j];
}
}
for(i1=g[x].begin();i1!=g[x].end();i1++)vis[*i1]=1;
M=0;
for(j=1;j<=N;j++){
if(!vis[f[j]])ot[++M]=f[j];
}
for(j=1;j<=M;j++)merge(x,ot[j]);
for(i1=g[x].begin();i1!=g[x].end();i1++)vis[*i1]=0;
for(i1=g[x].begin();i1!=g[x].end();i1++){
y=*i1;
for(i2=g[y].begin();i2!=g[y].end();i2++)vis[*i2]=1;
for(i2=g[x].begin();i2!=g[x].end();i2++){
if(!vis[*i2])merge(y,*i2);
}
for(i2=g[y].begin();i2!=g[y].end();i2++)vis[*i2]=0;
if((int)g[y].size()<M)
merge(x,y);
else{
for(i2=g[y].begin();i2!=g[y].end();i2++)vis[*i2]=1;
for(j=1;j<=M;j++){
if(!vis[ot[j]])merge(ot[j],y);
}
for(i2=g[y].begin();i2!=g[y].end();i2++)vis[*i2]=0;
}
}
for(it=v.begin();it!=v.end();it++){
x=it->x;
y=it->y;
d[x]=d[y]=0;
g[x].clear();
g[y].clear();
}
}
}
printf("%lld",ans);
}