【JZOJ5433】图
Description
有一个n个点A+B条边的无向连通图,有一变量x,每条边的权值都是一个关于x的简单多项式,其中有A条边的权值是k+x,另外B条边的权值是k-x,如果只保留权值形如k+x的边,那么这个图仍是一个连通图,如果只保留权值形如k-x的边,这个图也依然是一个连通图。
给出q组询问,每组询问给出x的值,问此时这个无向连通图的最小生成树权值是多少。
Solution
有一个肯(xian)定(ran)的结论:无论 x 的值是多少,最后的最小生成树的边一定是k+x边构成的图中的最小生成树上的边或k-x边构成的图中的最小生成树上的边。
于是,先把k+x图建出来最小生成树,将k-x中最小生成树的边拿出来排序,从小到大加进k+x图,每次把形成新环上最大的k+x边删掉,同时算出这条k-x边替代环上最大k+x边的下界,然后把这些下界排序,同时把询问挂上去查询即可。
这里可能加入的两条k-x边之间有交,但实际上没有关系,k小的那条边会先做出贡献。
删边加边可以用LCT维护。
Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define N 100010
#define ll long long
#define inf 2147483647
using namespace std;
struct node{
int u,v;
ll w;
}a[N*2],b[N*2];
int fa[N*5],son[N*5][2],mx[N*5],p[N*5],sz[N*5],rev[N*5];
ll va[N*5];
int pd(int x){
return x==son[fa[x]][1];
}
void update(int x){
int l=son[x][0],r=son[x][1];
mx[x]=x;
if(va[mx[l]]>va[mx[x]]) mx[x]=mx[l];
if(va[mx[r]]>va[mx[x]]) mx[x]=mx[r];
sz[x]=1+sz[l]+sz[r];
}
void put(int x){
if(!rev[x]) return;
swap(son[x][0],son[x][1]);
rev[son[x][0]]^=1,rev[son[x][1]]^=1;
rev[x]=0;
}
int d[N];
void down(int x,int y){
d[0]=0;
while(x!=y) d[++d[0]]=x,x=fa[x];
fd(i,d[0],1) put(d[i]);
}
void rotate(int x){
int y=fa[x],t=pd(x);
son[y][t]=son[x][1-t];
if(son[x][1-t]) fa[son[x][1-t]]=y;
fa[x]=fa[y];
if(fa[y]) son[fa[y]][pd(y)]=x;
else p[x]=p[y],p[y]=0;
son[x][1-t]=y,fa[y]=x;
update(y),update(x);
}
void splay(int x,int t){
down(x,t);
while(fa[x]!=t)
{
int y=fa[x];
if(fa[y]!=t)
if(pd(y)==pd(x)) rotate(y);
else rotate(x);
rotate(x);
}
}
void access(int x){
int y=0;
while(x)
{
splay(x,0);
fa[son[x][1]]=0,p[son[x][1]]=x;
son[x][1]=y,fa[y]=x,p[y]=0;
update(x),y=x,x=p[x];
}
}
void makeroot(int x){
access(x),splay(x,0),rev[x]^=1;
}
void link(int x,int y){
makeroot(x),p[x]=y;
}
void cut(int x,int y){
makeroot(x),access(y);
splay(y,0),son[y][0]=fa[x]=p[x]=0;
update(y);
}
bool check(int x,int y){
makeroot(x),access(y);
splay(x,0);
while(y && y!=x) y=fa[y];
if(!y) return false;
return true;
}
int query(int x,int y){
makeroot(x),access(y),splay(x,0);
return mx[x];
}
int fat[N];
int getfa(int x){
return fat[x]==x?fat[x]:fat[x]=getfa(fat[x]);
}
struct node2{
int x,t;
}Q[N];
bool cmp(node x,node y){
return x.w<y.w;
}
bool cmp2(node2 x,node2 y){
return x.x<y.x;
}
int z[N];
ll an[N];
int tot=0;
int main()
{
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
int n,A,B,q;
scanf("%d %d %d %d",&n,&A,&B,&q);
fo(i,0,n) va[i]=-inf;
fo(i,1,A)
scanf("%d %d %lld",&a[i].u,&a[i].v,&a[i].w);
sort(a+1,a+A+1,cmp);
int cn=0;
ll ans=0;
fo(i,1,A)
if(!check(a[i].u,a[i].v)){
cn++,ans+=a[i].w,va[i+n]=a[i].w;
link(a[i].u,i+n),link(i+n,a[i].v);
if(cn==n-1) break;
}
fo(i,1,B) scanf("%d %d %lld",&b[i].u,&b[i].v,&b[i].w);
sort(b+1,b+B+1,cmp);
fo(i,1,n) fat[i]=i;
cn=0;
fo(i,1,B)
{
int fx=getfa(b[i].u),fy=getfa(b[i].v);
if(fx!=fy)
{
fat[fy]=fx;
b[++cn]=b[i];
if(cn==n-1) break;
}
}
fo(i,1,cn)
{
int u=b[i].u,v=b[i].v,w=b[i].w;
int t=query(u,v);
if(t<=n) continue;
z[++tot]=w-va[t];
cut(a[t-n].u,t),cut(t,a[t-n].v);
link(u,v);
}
sort(z+1,z+tot+1);
fo(i,1,q) scanf("%d",&Q[i].x),Q[i].t=i;
sort(Q+1,Q+q+1,cmp2);
int pos=0;
ll tmp=0;
fo(i,1,q)
{
while(pos<tot && z[pos+1]<=Q[i].x*2) pos++,tmp+=z[pos];
an[Q[i].t]=ans+tmp+(ll)(n-1-pos)*Q[i].x-(ll)pos*Q[i].x;
}
fo(i,1,q) printf("%lld\n",an[i]);
}