zoukankan      html  css  js  c++  java
  • 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
    ",ans[i]);
    	return 0;
    }
    

    总结

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

  • 相关阅读:
    Manjaro 安装 VMware Pro 15
    RAID-0-10 搭建和使用
    列表的切换按钮,是什么实现的?
    动态表单的设计
    如何修改自定义表单的名字,不适用diyname,直接使用id
    php关于批量替换的测试
    fastadmin删除控制器,删除菜单,提示not found
    fastadmin自定义表单,如何根据字段信息,创建表,而且,可以随时修改字段的顺序
    fastadmin如何获取新增后的id,这个可以使用模型的钩子函数。
    fastadmin如何在弹窗内跳转?以及如何在非弹窗页面,做tab选项卡
  • 原文地址:https://www.cnblogs.com/jz-597/p/11579659.html
Copyright © 2011-2022 走看看