Description
有一个n个点A+B条边的无向连通图,有一变量x,每条边的权值都是一个关于x的简单多项式,其中有A条边的权值是k+x,另外B条边的权值是k-x,如果只保留权值形如k+x的边,那么这个图仍是一个连通图,如果只保留权值形如k-x的边,这个图也依然是一个连通图。
给出q组询问,每组询问给出x的值,问此时这个无向连通图的最小生成树权值是多少。
对于100%的数据,1<=n,q<=100000 , n-1<=A,B<=200000, 0<=k<=10^9 , -109<=v<=109
Solution
比赛时读优没有读入负号而爆零了- 显然是分成正边树和负边树。其他不在最小生成树中的边都是没有用的。
- 当x无限小的时候,一定取正边树,当x无限大的时候一定取负边树。
- 所以在这之中就存在一个将正边替换为负边的过程。
- 形象地考虑一下做最小生成树的过程,是将所有边排序。
- 当x是无限小的时候,正边边权的值域[l0,r0]与负边边权的值域[l1,r1]没有交。
- 但是当x逐渐变大的时候,正边中的有些边的边权比负边边权要大,而最先比某个正边边权要小的负边一定是k最小的。而它能替代的边理解成最小生成树的逆过程,肯定是替换掉最后加进去的,也就是k最大的正边。而且一条负边一定不会替换掉负边,这样一定不会更优。
- 所以我们只需要按照负边从小到大替换掉正边,对于每一个正边都在某一个时刻x被替换(意味着如果x在这个时刻后,那么这条边就被删掉,并加入了一条负边)。
- 用一个LCT维护MST的过程就好了。
- LCT时将边转化成点。查询x,y最大的边时,makeroot(x),access(y),那么x,y这条链就在splay树(x)中了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 100005
#define maxm 200005
#define maxt 1000005
#define inf (1e9+5)
#define ll long long
using namespace std;
const int null=0;
int n,m1,m2,q,i,j,k,fa[maxn],cnt,p[maxm],pi[maxm],que[maxn],qi[maxn];
struct edge{int x,y,z;} a[maxm],b[maxm];
int cmp(edge a,edge b){return a.z<b.z;}
int cmp2(int i,int j){return p[i]-a[i].z<p[j]-a[j].z;}
int cmp3(int i,int j){return que[i]<que[j];}
struct node{
int f,s[2],tag,mx,a;
} t[maxt];
int nroot(int x){return t[t[x].f].s[0]==x||t[t[x].f].s[1]==x;}
int get(int x){return t[t[x].f].s[1]==x;}
void update(int x){
int l=t[x].s[0],r=t[x].s[1]; t[x].mx=t[x].a;
if (l) t[x].mx=max(t[x].mx,t[l].mx);
if (r) t[x].mx=max(t[x].mx,t[r].mx);
}
void downtag(int x){
if (t[x].tag) {
swap(t[x].s[0],t[x].s[1]);
if (t[x].s[0]) t[t[x].s[0]].tag^=1;
if (t[x].s[1]) t[t[x].s[1]].tag^=1;
t[x].tag=0;
}
}
void rotate(int x){
int y=t[x].f,c=get(x);
t[y].s[c]=t[x].s[c^1]; t[x].s[c^1]=y;
if (t[y].s[c]!=null) t[t[y].s[c]].f=y;
if (nroot(y)) t[t[y].f].s[get(y)]=x;
t[x].f=t[y].f; t[y].f=x;
update(y); update(x);
}
int d[maxn];
void remove(int x){
while (x!=null){d[++d[0]]=x;if (!nroot(x)) break; x=t[x].f;}
while (d[0]) downtag(d[d[0]--]);
}
void splay(int x){
remove(x);
int y;
while (nroot(x)){
y=t[x].f;
if (nroot(y)){
if (get(x)==get(y)) rotate(y);
else rotate(x);
}
rotate(x);
}
update(x);
}
void access(int x){
int y=null;
for(;x!=null;x=t[x].f){
splay(x);
t[x].s[1]=y;
update(y=x);
}
}
void makeroot(int x){access(x);splay(x);t[x].tag=1;}
void link(int x,int y){makeroot(x);t[x].f=y;access(x);}
void cut(int x,int y){makeroot(x);access(y);splay(y);t[t[y].s[0]].f=null;t[y].s[0]=null;update(y);}
int read(){
int x=0,tp=1; char ch=getchar();
for(;(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
if (ch=='-') tp=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*tp;
}
int father(int x){return (fa[x]==x)?x:fa[x]=father(fa[x]);}
int link_fa(edge e){
int x=father(e.x),y=father(e.y);
if (x!=y) {fa[x]=y;return 1;}
return 0;
}
void Predo(edge *a,int m){
for(i=1;i<=m;i++) a[i].x=read(),a[i].y=read(),a[i].z=read();
sort(a+1,a+1+m,cmp);
for(i=1;i<=n;i++) fa[i]=i; cnt=0;
for(i=1;i<=m;i++) if (link_fa(a[i])){
a[++cnt]=a[i];
if (cnt==n-1) break;
}
}
void Maketree(){
for(i=1;i<=3*n-2;i++) t[i].mx=t[i].a=-inf;
for(i=1;i<n;i++) {
int x=a[i].x,y=a[i].y; t[n+i].mx=t[n+i].a=a[i].z;
link(x,n+i),link(n+i,y);
}
}
int find(int x){
downtag(x);
if (t[x].a==t[x].mx) return x;
int l=t[x].s[0],r=t[x].s[1];
if (l&&t[l].mx==t[x].mx) return find(l);
else return find(r);
}
void ADD(){
for(i=1;i<n;i++) {
int x=b[i].x,y=b[i].y;
makeroot(x),access(y);
splay(x);
int k=find(x);
int xx=a[k-n].x,yy=a[k-n].y;
cut(k,xx),cut(k,yy);
p[k-n]=b[i].z;
link(n+(n-1)+i,x),link(n+(n-1)+i,y);
}
}
ll Ans[maxn];
void GETANS(){
for(i=1;i<n;i++) pi[i]=i;
sort(pi+1,pi+n,cmp2);
for(i=1;i<=q;i++) que[i]=read(),qi[i]=i;
sort(qi+1,qi+1+q,cmp3);
ll ans=0; k=1;
for(i=1;i<n;i++) ans+=a[i].z;
for(i=1;i<=q;i++){
for(;k<n&&p[pi[k]]-a[pi[k]].z<que[qi[i]]*2;k++) ans+=p[pi[k]]-a[pi[k]].z;
Ans[qi[i]]=ans+1ll*que[qi[i]]*((n-1-k+1)-(k-1));
}
for(i=1;i<=q;i++) printf("%lld
",Ans[i]);
}
int main(){
n=read(),m1=read(),m2=read(),q=read();
Predo(a,m1),Predo(b,m2);
Maketree();
ADD();
GETANS();
}