zoukankan      html  css  js  c++  java
  • 【洛谷P2387】魔法森林

    题目

    题目链接:https://www.luogu.com.cn/problem/P2387
    为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐士。魔法森林可以被看成一个包含 (n) 个节点 (m) 条边的无向图,节点标号为 (1,2,3,…,n),边标号为 (1,2,3,…,m)。初始时小 E 同学在 (1) 号节点,隐士则住在 (n) 号节点。小 E 需要通过这一片魔法森林,才能够拜访到隐士。
    魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪 就会对其发起攻击。幸运的是,在 (1) 号节点住着两种守护精灵:A 型守护精灵与 B 型守护精灵。小 E 可以借助它们的力量,达到自己的目的。
    只要小 E 带上足够多的守护精灵,妖怪们就不会发起攻击了。具体来说,无向图中的每一条边 (e_i) 包含两个权值 (a_i)(b_i) 。若身上携带的 A 型守护精灵个数不少于 (a_i) ,且 B 型守护精灵个数不少于 (b_i) ,这条边上的妖怪就不会对通过这条边的人发起攻击。当且仅当通过这片魔法森林的过程中没有任意一条边的妖怪向 小 E 发起攻击,他才能成功找到隐士。
    由于携带守护精灵是一件非常麻烦的事,小 E 想要知道,要能够成功拜访到 隐士,最少需要携带守护精灵的总个数。守护精灵的总个数为 A 型守护精灵的个数与 B 型守护精灵的个数之和。

    思路

    将边按照 (a) 排序,从小到大枚举 (a),将对应的边的当做一个点扔进 LCT 内,在 LCT 上连接其在原图中所连接的两个点。
    显然随着 (a) 的增加,最优路径上 (b) 的最大值一定是逐渐减小的,所以我们需要用 LCT 维护 (b) 的最小生成树。
    具体的,当我们要加入边 (i) 时,如果 (i) 所连接的两个点在 LCT 上已经属于同一棵 Splay 了,那么我们就把 (u)(v) 的链单独拉出来形成一棵 Splay,然后查询 Splay 上 (b) 权的最大值所对应的边。然后判断是当前加入的边的边权更小还是原边更小,如果当前边更小则 Cut 原边,Link 新的边即可。
    每次操作后将 (1,n) 所对应的链拉出来,更新答案即可。
    时间复杂度 (O((n+m)log n))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=150010,Inf=1e9;
    int n,m,ans;
    
    struct edge
    {
    	int u,v,a,b;
    }e[N];
    
    bool cmp(edge x,edge y)
    {
    	return x.a<y.a;
    }
    
    struct LCT
    {
    	int maxb[N],val[N],ch[N][2],fa[N];
    	bool rev[N];
    	
    	void pushdown(int x)
    	{
    		if (rev[x])
    		{
    			int lc=ch[x][0],rc=ch[x][1];
    			swap(ch[lc][0],ch[lc][1]);
    			swap(ch[rc][0],ch[rc][1]);
    			rev[lc]^=1; rev[rc]^=1; rev[x]=0;
    		}
    	}
    	
    	void pushup(int x)
    	{
    		int lc=maxb[ch[x][0]],rc=maxb[ch[x][1]],k=val[x];
    		if (e[k].b>=e[lc].b && e[k].b>=e[rc].b) maxb[x]=k;
    		if (e[lc].b>=e[k].b && e[lc].b>=e[rc].b) maxb[x]=lc;
    		if (e[rc].b>=e[k].b && e[rc].b>=e[lc].b) maxb[x]=rc;
    	}
    	
    	int pos(int x)
    	{
    		return ch[fa[x]][1]==x;
    	}
    	
    	int notrt(int x)
    	{
    		return ch[fa[x]][1]==x || ch[fa[x]][0]==x;
    	}
    	
    	void rotate(int x)
    	{
    		int y=fa[x],z=fa[y],k=pos(x),c=ch[x][k^1];
    		if (notrt(y)) ch[z][pos(y)]=x; ch[x][k^1]=y; ch[y][k]=c;
    		if (c) fa[c]=y; fa[y]=x; fa[x]=z;
    		pushup(y); pushup(x);
    	}
    	
    	void splay(int x)
    	{
    		stack<int> st;
    		st.push(x);
    		for (int y=x;notrt(y);y=fa[y]) st.push(fa[y]);
    		for (;st.size();st.pop()) pushdown(st.top());
    		while (notrt(x))
    		{
    			if (notrt(fa[x]))
    				rotate(pos(x)==pos(fa[x])?fa[x]:x);
    			rotate(x);
    		}
    		pushup(x);
    	}
    	
    	void access(int x)
    	{
    		for (int y=0;x;y=x,x=fa[x])
    		{
    			splay(x); ch[x][1]=y;
    			pushup(x);
    		}
    	}
    	
    	int findrt(int x)
    	{
    		access(x); splay(x);
    		for (;ch[x][0];x=ch[x][0])
    			pushdown(x);
    		splay(x);
    		return x;
    	}
    	
    	void makert(int x)
    	{
    		access(x); splay(x);
    		swap(ch[x][0],ch[x][1]); rev[x]^=1;
    	}
    	
    	void split(int x,int y)
    	{
    		makert(x); access(y);
    		splay(y);
    	}
    	
    	void link(int x,int y)
    	{
    		makert(x);
    		if (findrt(y)!=x) fa[x]=y;
    		splay(x);
    	}
    	
    	void cut(int x,int y)
    	{
    		makert(x);
    		if (findrt(y)!=x) return;
    		splay(x);
    		if (!ch[y][0] && fa[y]==x)
    			fa[y]=ch[x][1]=0;
    		pushup(x);
    	}
    }lct;
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=m;i++)
    		scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].a,&e[i].b);
    	sort(e+1,e+1+m,cmp);
    	ans=Inf;
    	for (int i=1;i<=m;i++)
    	{
    		int u=e[i].u,v=e[i].v;
    		if (lct.findrt(u)!=lct.findrt(v))
    		{
    			lct.link(u,n+i); lct.link(v,n+i);
    			lct.val[n+i]=lct.maxb[n+i]=i;
    		}
    		else
    		{
    			lct.split(u,v);
    			int id=lct.maxb[v];
    			if (e[id].b>e[i].b)
    			{
    				lct.cut(id+n,e[id].u); lct.cut(id+n,e[id].v);
    				lct.link(n+i,u); lct.link(n+i,v);
    				lct.val[n+i]=lct.maxb[n+i]=i;
    			}
    		}
    		if (lct.findrt(1)==lct.findrt(n))
    		{
    			lct.split(1,n);
    			ans=min(ans,e[lct.maxb[n]].b+e[i].a);
    		}
    	}
    	if (ans<Inf) printf("%d",ans);
    		else printf("-1");
    	return 0;
    }
    
  • 相关阅读:
    联赛前第七阶段总结
    tomcat启动服务一会后自动关闭
    删除软件服务
    jmeter ramp-up解释
    mysql数据库报错1045
    tomcat在linux上的安装
    ant在linux下的安装部署
    查看一条mysql语句的性能
    linux下安装svn服务器
    InfluxDB数据库报错ERR: unable to parse authentication credentials Warning: It is possible this error is due to not setting a database. Please set a database with the command "use <database>".
  • 原文地址:https://www.cnblogs.com/stoorz/p/14243340.html
Copyright © 2011-2022 走看看