NOIP模拟 图【最小生成树】

来源:互联网 发布:易语言写软件 编辑:程序博客网 时间:2024/06/05 19:07

题目大意:

给n个点,m1条A边,m2条B边,A,B边所构成的图都是联通的;
有q次询问,每次询问给出x,问将所有A边加上x,B边减去x后图中的最小生成树是多少。1<=n,q<=1000;

解题思路:

注意到当x为负无穷时答案就是A边构成的MST,为正无穷时就是B边构成的MST,所以当x逐渐增大,B边构成的MST上的边就会逐渐取代A边构成的MST上的边。而最优的肯定是小的B边先加进去取代路径上最大的A边。

所以我们可以按B边从小到大的顺序算出加进每条B边时的x的下界,同时不断修改树的形态即可。而且这样如果两条B边影响的路径有交集,其x的下界是递增的,而如果没有交集,x虽不是递增的,但不影响另一条边所替换的A边,可是会影响当前MST的大小。所以最后才能把替换操作按x从小到大排序后计算当前x下MST的大小。

然后把询问也按x从小到大排序后计算当前x下MST的大小即可。

由于修改树的形态是O(n)的(LCT可优化到O(logn)),所以做复杂度为O(n2)

#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<algorithm>#include<cmath>#include<vector>#include<queue>#define ll long longusing namespace std;int getint(){    int i=0,f=1;char c;    for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());    if(c=='-')f=-1,c=getchar();    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';    return i*f;}const int N=100005,M=200005,INF=0x3f3f3f3f;int n,m1,m2,Q;int fa[N],len[N],stk[N],visit[N];int tot,first[N],nxt[M],to[M],w[M];ll totlen,ans[N];bool exist[M];struct edge{    int x,y,w;    friend inline bool operator <(const edge &a,const edge &b)    {return a.w<b.w;}}bian1[M],bian2[M];struct node{    int x,w1,w2;ll val;    friend inline bool operator <(const node &a,const node &b)    {return a.x<b.x;}}upt[M];struct node1{    int x,id;    friend inline bool operator <(const node1 &a,const node1 &b)    {return a.x<b.x;}}q[N];int find(int x){    return fa[x]==x?x:fa[x]=find(fa[x]);}void add(int x,int y,int z){    nxt[++tot]=first[x],first[x]=tot,to[tot]=y,w[tot]=z;}void kruskal1(){    for(int i=1;i<=n;i++)fa[i]=i;    sort(bian1+1,bian1+m1+1);    int k=0;    for(int i=1;i<=m1;i++)    {        int x=find(bian1[i].x),y=find(bian1[i].y);        if(x!=y)        {            k++;totlen+=bian1[i].w;            add(bian1[i].x,bian1[i].y,bian1[i].w);            add(bian1[i].y,bian1[i].x,bian1[i].w);            fa[y]=x;        }        if(k==n-1)break;    }}void kruskal2(){    for(int i=1;i<=n;i++)fa[i]=i;    sort(bian2+1,bian2+m2+1);    int k=0;    for(int i=1;i<=m2;i++)    {        int x=find(bian2[i].x),y=find(bian2[i].y);        if(x!=y)k++,exist[i]=true,fa[y]=x;        if(k==n-1)break;    }    int cnt=0;    for(int i=1;i<=m2;i++)        if(exist[i])bian2[++cnt]=bian2[i];    m2=cnt;    memset(fa,0,sizeof(fa));}void dfs(int u){    for(int e=first[u];e;e=nxt[e])    {        int v=to[e];        if(v==fa[u])continue;        fa[v]=u;len[v]=w[e];        dfs(v);    }}void solve(int i){    int x=bian2[i].x,y=bian2[i].y,mx=-INF,pos,top,bz=2;    visit[x]=i;    while(fa[x])visit[fa[x]]=i,x=fa[x];    while(visit[y]!=i)    {        if(len[y]>mx)mx=len[y],pos=y,bz=2;        y=fa[y];    }    x=bian2[i].x;    while(x!=y)    {        if(len[x]>mx)mx=len[x],pos=x,bz=1;        x=fa[x];    }    upt[i].w1=mx,upt[i].w2=bian2[i].w,upt[i].x=(bian2[i].w-mx+1)/2;    x=bian2[i].x,y=bian2[i].y,top=0;    if(bz==2)swap(x,y);    while(x!=fa[pos])stk[++top]=x,x=fa[x];    for(int j=top;j>1;j--)fa[stk[j]]=stk[j-1],len[stk[j]]=len[stk[j-1]];    x=stk[1],fa[x]=y,len[x]=-INF;}int main(){    //freopen("mst.in","r",stdin);    //freopen("mst.out","w",stdout);    n=getint(),m1=getint(),m2=getint(),Q=getint();    for(int i=1;i<=m1;i++)bian1[i].x=getint(),bian1[i].y=getint(),bian1[i].w=getint();    for(int i=1;i<=m2;i++)bian2[i].x=getint(),bian2[i].y=getint(),bian2[i].w=getint();    kruskal1(),kruskal2(),dfs(1);    for(int i=1;i<=m2;i++)        solve(i);    sort(upt+1,upt+n);    upt[0].x=-INF,upt[0].val=totlen-1ll*(n-1)*INF,upt[n].x=INF;    for(int i=1;i<n;i++)        upt[i].val=upt[i-1].val+1ll*(n-i*2+1)*(upt[i].x-upt[i-1].x)-(upt[i].w1+upt[i].x)+(upt[i].w2-upt[i].x);    for(int i=1;i<=Q;i++)q[i].x=getint(),q[i].id=i;    sort(q+1,q+Q+1);    int p=0;    for(int i=1;i<=Q;i++)    {        while(q[i].x>=upt[p+1].x)p++;        ans[q[i].id]=upt[p].val+1ll*(n-p*2-1)*(q[i].x-upt[p].x);    }    for(int i=1;i<=Q;i++)        cout<<ans[i]<<'\n';    return 0;}
原创粉丝点击