想到最小生成树的sort+并查集算法,于是可以顺便用并查集维护点所在的连通分量的点的数目(不知道学名是不是这么说),记为poi[v];
然后当边权限制为f[i].w时,其答案为ww[i]=ww[i-1]-(poi[u]-1)*poi[u]-(poi[v]-1)*poi[v]+(poi[u]+poi[v])*(poi[u]+poi[v]-1);
之后连通u,v,并将新的poi[u]更新至并查集的树根。这样就相当于用最小生成树+并查集打了个表了,对于询问二分即可
自己YY的方法,很多地方丑
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int INF=0x7f7f7f7f; const int maxm=100008; const int maxn=20008; struct fuck{ int u,v,w; bool operator<(const fuck &a) const { return w<a.w; } }f[maxm<<1]; struct shit{ int u,w; }ww[maxm]; int poi[maxn],uni[maxn]; int find(int x) { if(uni[x]!=x) { uni[x]=find(uni[x]); poi[x]=max(poi[x],poi[uni[x]]); return uni[x]; } return x; } int bs(int w,int idx) { ww[idx].u=INF; int left=1,right=idx; while(left<right) { int mid=(right+left)>>1; if(ww[mid].u>w) right=mid; else left=mid+1; } return left-1; } void update(int x) { if(uni[x]!=x) { poi[uni[x]]=max(poi[x],poi[uni[x]]); find(uni[x]); } } int main() { int t,i,j,n,m,q,u,v,w; scanf("%d",&t); while(t--) { scanf("%d%d%d",&n,&m,&q); for(i=1;i<=m;i++) { scanf("%d%d%d",&u,&v,&w); f[i].u=u;f[i].v=v;f[i].w=w; } sort(f+1,f+1+m); for(i=1;i<=n;i++) { uni[i]=i; poi[i]=1; } int idx=1; ww[0].w=0; for(i=1;i<=m;i++) { if(find(f[i].u)!=find(f[i].v)) { ww[idx].w=ww[idx-1].w; poi[f[i].u]=max(poi[f[i].u],poi[uni[f[i].u]]); poi[f[i].v]=max(poi[f[i].v],poi[uni[f[i].v]]); uni[uni[f[i].u]]=uni[f[i].v]; if(poi[f[i].u]>1) ww[idx].w-=poi[f[i].u]*(poi[f[i].u]-1); if(poi[f[i].v]>1) ww[idx].w-=poi[f[i].v]*(poi[f[i].v]-1); poi[f[i].u]+=poi[f[i].v]; poi[f[i].v]=poi[f[i].u]; update(f[i].u); update(f[i].v); ww[idx].u=f[i].w; ww[idx++].w+=poi[f[i].u]*(poi[f[i].u]-1); } } for(i=1;i<=q;i++) { scanf("%d",&w); int asd=bs(w,idx); printf("%d ",ww[asd].w); } } return 0; }