NOI2014 魔法森林 [LCT+贪心]

题目大意:给出一个无向图,每条边i有两个值ai、bi。要求出一条从1到n的路,使得路上经过的a的最大值(设为A)与b的最大值(设为B)的和尽量小。

思路:容易想到逐步往图中加入边的做法,但是这一题存在两个关键值,所以我们还需要利用一点点贪心的思想来权衡答案。

            我们考虑按a值从小到大加入每条边i,每一次求出A不超过ai的情况下,1的n的路径上B的最小值。

            那么我们只需要维护以b值为权值的一棵包含最优解的树就可以了:

            当前加入边i,如果没有形成环,那么直接连接xi,yi。如果形成环,找到环上b值最大的边,将它删去即可。

            这样,我们每一次询问1到n在树上的路径上的B值就可以了。

            这棵不断删边、连边的树可以用LCT来维护,大功告成。

  注意:1.LCT有一些需要注意的地方,比如updata不能少,比如rotate有一点点区别于splay等;

            2.这题有重边、自环!

            这些注意点都已在程序中用attention标出。

#include 
#include 
#include 
#define lf ch[x][0]
#define rg ch[x][1]
#define rep(i,j,k) for (i=j;i<=k;i++)
using namespace std;
const int N=2e5+5,M=1e5+5;
int n,m,i,u,v,fx,fy,wh,tmp,ans;
struct arr{
	int x,y,a,b;
}e[M];
bool cmp(arr A,arr B) {
	return A.aw[ms[x]]) ms[x]=ms[lf];
		if (rg && w[ms[rg]]>w[ms[x]]) ms[x]=ms[rg];
	}
	void push(int x) { if (!rev[x]) return;
		rev[x]=0; swap(ch[x][0],ch[x][1]);
		rev[lf]^=1; rev[rg]^=1;
	}
	void rotate(int x)
	{
		int f=fa[x],gf=fa[fa[x]],sd=side(x),fsd=side(f),son=ch[x][sd^1],flg=isroot(fa[x]);
		fa[f]=x; fa[x]=gf; if (!flg) ch[gf][fsd]=x; //attention >_<
		fa[son]=f; ch[f][sd]=son; ch[x][sd^1]=f;
		updata(f); updata(x);
	}
	void splay(int x)
	{
		int y=x;
		for(tp=0;!isroot(y);y=fa[y]) stk[++tp]=y;
		for (stk[++tp]=y;tp;tp--) push(stk[tp]); //attention ._.
		while (!isroot(x))
		{
			if (isroot(fa[x])) rotate(x);
			else {
				if (side(x)==side(fa[x])) rotate(fa[x]),rotate(x);
				else rotate(x),
				rotate(x);
			}
		}
	}
	void access(int x)
	{
		int y=0;
		while (1) {
			splay(x);
			ch[x][1]=y; updata(x); //attention ._.
			y=x; x=fa[x];
			if (!x) return ;
		}
	}
	void makeroot(int x) {
		access(x); splay(x); rev[x]^=1;
	}
	void link(int u,int v) {
		makeroot(u); fa[u]=v;
	}
	void cut(int u,int v) {
		makeroot(u); access(v); splay(v);
		ch[v][0]=0; fa[u]=0; updata(v); //attention ._.
	}
	int query(int u,int v) {
		makeroot(v); access(u); splay(v); return ms[v];
	}
	bool smt(int u,int v) {
		makeroot(u); makeroot(v);
		return !isroot(u);
	}
}tr;
void read(int &ret)
{
	char ch; ret=0;
	for (ch=getchar();ch<'0' || ch>'9';ch=getchar());
	for (;ch>='0' && ch<='9';ch=getchar()) ret=ret*10+ch-'0';
}
int main()
{
	read(n); read(m);
	rep(i,1,m) read(e[i].x),read(e[i].y),read(e[i].a),read(e[i].b);
	sort(e+1,e+1+m,cmp);
	rep(i,1,n+m) tr.ms[i]=i;
	rep(i,n+1,n+m) tr.w[i]=e[i-n].b;
	ans=1e9;
	rep(i,1,m)
	{
		tmp=e[i].a; u=e[i].x; v=e[i].y; if (u==v) continue; //attention-_-
		if (!tr.smt(u,v)) {
			tr.link(u,n+i); tr.link(n+i,v);
		}
		else {
			wh=tr.query(u,v);
			if (tr.w[wh]>e[i].b)
			{
				tr.cut(e[wh-n].x,wh); tr.cut(e[wh-n].y,wh);
				tr.link(u,n+i);       tr.link(n+i,v);
			}
		}
		if (tr.smt(1,n))
		{
			tmp+=tr.w[tr.query(1,n)];
			ans=min(ans,tmp);
		}
	}
	if (ans==1e9) ans=-1;
	printf("%d\n",ans);
	return 0;
}


你可能感兴趣的:(数据结构)