5433. 【NOIP2017提高A组集训10.28】图

题目

题目大意

给你一个无向连通图,有两种边,为\(k-x\)\(k+x\)的形式。
有一堆询问,问\(x\)为某个值的时候的最小生成树。


思考历程

有过一些猜想,但被自己推翻了。
没有想出来,于是只能打暴力。


正解

首先有个比赛时就想到的一个很显然的结论:
肯定是正边和负边分别做一次最小生成树,然后用这些树边来生成新的最小生成树。
当时我是这么想的:可以先对正边做一次最小生成树,然后一条一条负边加进去,可以用\(LCT\)维护。最终形成的一定是最优的。所以只有正边的最小生成树的边有意义。反过来,只有负边的最小生成树的边有意义。
于是就只有两棵树的边有意义。

所以就先做一遍最小生成树。然后对正边建立\(LCT\)
将负边排序,一个一个加进去。每次替换路径上权值最大的正边,并且记录替换的时候的\(x\)的下界。
搞完之后将\(x\)的下界排个序,就可以得到各个时段的最小生成树。

为什么是正确的呢?
如果有两条边,它们端点覆盖的路径的最大边是相同的(在没有进行任何加边操作和删边操作的时候)。很显然,更小的那个会优先替代(时间上也是它更小),而更大的那个会替代路径上其它的比较大的边。


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
#define M 200010
#define INF 2000000000
#define ll long long
int n,A,B;
struct edge{
    int u,v,len;
} ea[M],eb[M];
bool cmpe(const edge &a,const edge &b){return a.len<b.len;}
int ufs[N];
int getufs(int x){return ufs[x]==x?x:ufs[x]=getufs(ufs[x]);}
inline void init(edge *e,int &m){
    for (int i=1;i<=m;++i)
        scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].len);
    sort(e+1,e+m+1,cmpe);
    int tmp=0;
    for (int i=1;i<=n;++i)
        ufs[i]=i;
    for (int i=1;i<=m;++i){
        int x=getufs(e[i].u),y=getufs(e[i].v);
        if (x!=y){
            ufs[x]=y;
            e[++tmp]=e[i];
        }
    }
    m=tmp;
}
struct Node *null;
struct Node{
    Node *fa,*c[2];
    bool rev,isr;
    int len;
    Node *mx;
    inline void rvs(){swap(c[0],c[1]),rev^=1;}
    inline void push(){if (!isr) fa->push();if (rev) rev=0,c[0]->rvs(),c[1]->rvs();}
    inline bool getson(){return fa->c[0]!=this;}
    inline void upd(){mx=(c[0]->mx->len>c[1]->mx->len?c[0]->mx:c[1]->mx),mx=(mx->len<len?this:mx);}
    inline void rotate(){
        Node *y=fa;
        if (y->isr)
            y->isr=0,isr=1;
        else
            y->fa->c[y->getson()]=this;
        bool k=getson();
        fa=y->fa;
        y->c[k]=c[!k],c[!k]->fa=y;
        c[!k]=y,y->fa=this;
        mx=y->mx,y->upd();
    }
    inline void splay(){
        push();
        for (;!isr;rotate())
            if (!fa->isr)
                getson()!=fa->getson()?rotate():fa->rotate();
    }
    inline Node *access(){
        Node *x=this,*y=null;
        for (;x!=null;y=x,x=x->fa){
            x->splay();
            x->c[1]->isr=1;
            y->isr=0;
            x->c[1]=y;
            x->upd();
        }
        return y;
    }
    inline Node *mroot(){
        Node *res=access();
        res->rvs();
        return res;
    }
    inline void link(Node *y){y->mroot()->fa=this;}
} d[N],eda[N],edb[N];
int tim[N],con[N],p[N];
bool cmpp(int a,int b){return tim[a]<tim[b];}
struct Query{
    int x,num;
} q[N];
bool cmpq(Query a,Query b){return a.x<b.x;}
ll ans[N];
int main(){
    freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);
    int Q;
    scanf("%d%d%d%d",&n,&A,&B,&Q);
    init(ea,A),init(eb,B);
    null=new Node;
    *null={null,null,null,0,0,-INF,null};
    for (int i=1;i<=n;++i)
        d[i]={null,null,null,0,1,-INF,null};
    for (int i=1;i<=A;++i){
        eda[i]={null,null,null,0,1,ea[i].len,&eda[i]};
        d[ea[i].u].link(&eda[i]);
        d[ea[i].v].link(&eda[i]);
    }
    ll sum=0;
    for (int i=1;i<=A;++i)
        sum+=ea[i].len;
    tim[0]=-INF;
    con[0]=0;
    for (int i=1;i<=B;++i){
        p[i]=i;
        int u=eb[i].u,v=eb[i].v;
        d[u].mroot();
        Node *t=d[v].access();
        if (t->mx->len==-INF){
            tim[i]=INF;
            continue;
        }
        tim[i]=(eb[i].len-t->mx->len-1>>1)+1;
        con[i]=eb[i].len-t->mx->len;
        t=t->mx;
        d[ea[t-eda].u].mroot();
        d[ea[t-eda].v].access();
        t->splay();
        t->c[0]->isr=t->c[1]->isr=1;
        t->c[0]->fa=t->c[1]->fa=null;
        edb[i]={null,null,null,0,1,-INF,null};
        edb[i].link(&d[u]);
        edb[i].link(&d[v]);
    }
    sort(p+1,p+B+1,cmpp);
    for (int i=1;i<=Q;++i)
        scanf("%d",&q[i].x),q[i].num=i;
    sort(q+1,q+Q+1,cmpq);
    for (int i=1,j=1,k=n-1;i<=Q;++i){
        for (;j<=B && tim[p[j]]<=q[i].x;++j){
            sum+=con[p[j]];
            k-=2;
        }
        ans[q[i].num]=sum+(ll)k*q[i].x;
    }
    for (int i=1;i<=Q;++i)
        printf("%lld\n",ans[i]);
    return 0;
}

总结

有了结论,一切都好说……
我一开始就应该猜结论,拍验证……

转载于:https://www.cnblogs.com/jz-597/p/11579659.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值