题面
把图分成正图(k+x)和负图(k-x),且称正树为正图中的最小生成树,负树为负图中的最小生成树。
有个很明显的结论是,无论 x 的值是多少,最后的最小生成树的边一定是正图中的最小生成树上的边或负图的最小生成树上的边(因为正图和负图都是联通图)。
当 x 趋近于无穷小的时候,正树就是图的最小生成树,当 x 逐渐增大,负树中的边会逐渐取代正树中的边(但不一定按照从小到大顺序取代),例如样例。
但是这样并不影响我们从小到大加入负树中的边,因为上面的情况只会出现在两边产生的环没有边相交的情况,而如果有相交的部分,那么 k 比较小的会先做出决策。
然后就得到如下算法:
先分别做最小生成树算法得到正树和负树,以正树作为初始版本的最小生成树,然后将负树上的边从小到大加进生成树中,对于一条负边
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;
const int maxn=100010;
const int maxm=200010;
const int infk=1000000010;
int n,a,b,Q,t,fa[maxn];
ll sum[maxm];
struct edge
{
int t,k,f;
bool h;
edge *next,*rev;
}*con[maxn];
struct node
{
int u,v,k,f;
edge *pt;
}ea[maxm],eb[maxm],e[maxm];
struct node2
{
ll s;
int lx,tp;
}d[maxm];
int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
void ins(int x,int y,int k,int f,bool h)
{
edge *p=new edge;
p->t=y;
p->k=k;
p->f=f;
p->h=h;
p->next=con[x];
con[x]=p;
}
bool cmp(node a,node b)
{
return a.k+a.f*t<b.k+b.f*t;
}
bool cmp2(node2 a,node2 b)
{
return a.lx<b.lx;
}
int getfa(int v)
{
if(fa[v]==v) return v;
fa[v]=getfa(fa[v]);
return fa[v];
}
void MST0(node *ex,int &sz)
{
int tot=0,chs=0;
sort(ex+1,ex+sz+1,cmp);
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=sz;i++)
if(getfa(ex[i].u)!=getfa(ex[i].v))
{
fa[getfa(ex[i].u)]=getfa(ex[i].v);
ex[++tot]=ex[i];
chs++;
if(chs>=n-1) break;
}
sz=tot;
}
ll MST(int &ttp)
{
sort(e+1,e+a+b+1,cmp);
int chs=0;ttp=0;ll ans=0;
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=a+b;i++)
if(getfa(e[i].u)!=getfa(e[i].v))
{
fa[getfa(e[i].u)]=getfa(e[i].v);
ans+=e[i].k+e[i].f*t;
if(e[i].f==1) ttp++;
chs++;
if(chs>=n-1) break;
}
return ans;
}
bool find(int v,int fa,int goal,edge *&mxe)
{
bool re=0;
for(edge *p=con[v];p;p=p->next)
if(p->t!=fa&&p->h)
{
if(p->t==goal) re=1;
else {re=re||find(p->t,v,goal,mxe);}
if(re)
{
if((mxe==NULL||p->k>mxe->k)&&p->f==1) mxe=p;
return 1;
}
}
return 0;
}
int main()
{
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
n=read();a=read();b=read();Q=read();
for(int i=1;i<=a;i++)
ea[i].u=read(),ea[i].v=read(),ea[i].k=read(),ea[i].f=1;
for(int i=1;i<=b;i++)
eb[i].u=read(),eb[i].v=read(),eb[i].k=read(),eb[i].f=-1;
if(n>1000)
{
for(int i=1;i<=a;i++)
e[i]=ea[i];
t=b=0;
ll base=MST(b);
while(Q--)
{
t=read();
printf("%lld\n",base+(ll)t*(n-1));
}
}
else
{
t=0;
MST0(ea,a);
MST0(eb,b);
for(int i=1;i<=a;i++)
{
e[i]=ea[i];
ins(ea[i].u,ea[i].v,ea[i].k,ea[i].f,1);
ins(ea[i].v,ea[i].u,ea[i].k,ea[i].f,1);
con[ea[i].u]->rev=con[ea[i].v];
con[ea[i].v]->rev=con[ea[i].u];
ea[i].pt=con[ea[i].u];
}
for(int i=1;i<=b;i++)
{
e[i+a]=eb[i];
ins(eb[i].u,eb[i].v,eb[i].k,eb[i].f,0);
ins(eb[i].v,eb[i].u,eb[i].k,eb[i].f,0);
con[eb[i].u]->rev=con[eb[i].v];
con[eb[i].v]->rev=con[eb[i].u];
eb[i].pt=con[eb[i].u];
}
t=-1e9;d[0].s=MST(d[0].tp);d[0].lx=t;
for(int i=1;i<=b;i++)
{
edge *mxe=NULL;
find(eb[i].u,0,eb[i].v,mxe);
mxe->h=mxe->rev->h=0;
eb[i].pt->h=eb[i].pt->rev->h=1;
t=ceil((double)(eb[i].k-mxe->k)/2+0.01);
d[i].s=MST(d[i].tp);d[i].lx=t;
d[i].tp-=(n-1-d[i].tp);
}
sort(d,d+b+1,cmp2);
while(Q--)
{
t=read();
int l=0,r=b+1;
while(l<r)
{
int mid=(l+r)>>1;
if(d[mid].lx<=t) l=mid+1;
else r=mid;
}
l--;
printf("%lld\n",d[l].s+(ll)d[l].tp*(t-d[l].lx));
}
}
return 0;
}