NOIP模拟 图【最小生成树】

题目大意:

给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
#include
#include
#include
#include
#include
#include
#include
#define ll long long
using 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.wstruct node
{
    int x,w1,w2;ll val;
    friend inline bool operator <(const node &a,const node &b)
    {return a.xstruct node1
{
    int x,id;
    friend inline bool operator <(const node1 &a,const node1 &b)
    {return a.xint 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;i1].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<'\n';
    return 0;
}

你可能感兴趣的:(最小生成树)