码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj-2001 City 城市建设

时间:2015-11-11 16:35:20      阅读:545      评论:0      收藏:0      [点我收藏+]

标签:bzoj   分治   线段树   lct   最小生成树   

题意:

给出一个n个结点m条边的带权无向连通图,有q次操作;

每次操作是修改一个边的权值,要求每次操作后输出这个图中最小生成树的权值和;

n<=20000,m<=50000,q<=50000;


题解:

网上题解都是那些鬼畜的分治做法,每层求最小生成树将问题缩小到可以接受的范围;

不过那个方法不好理解并且难以推广,所以wyfcyx大爷提出了一种更加让人愉悦的做法;

首先这个问题不能直接用LCT维护,因为当删去一条边(边的权值变大)之后,我们无法知道这两个连通块之间是否还有更小边相连;

也就是说,LCT维护最小生成树不能删边,所以利用分治做一个转化;

我们考虑一个时间轴,每一条边作为一个区间覆盖了它存在的时间段;

那么对于一个询问的时间点,当时在这个图上的边即为覆盖在点上面的那些;

所以用线段树的姿势,将每个边的区间拆成不超过log个,然后挂在那个线段树结点上;

现在从根到某个线段树的叶子,将所有经过结点挂的边一一加入LCT跑最小生成树,跑到最下面就是对那个时间点的答案;

而因为要查询全部,那就应当在深搜回溯的时候,删除刚才插入的边,然后返回父结点,再向另一个子结点搜索;

但是LCT并不能支持删边啊。。。也并不支持向左右子树各自可持久化的分叉出一个版本。。。

然而wyfcyx大爷出现了!因为LCT的操作是十分简单的,只有Link和Cut,并且恰为相反操作;

因此记录每次加入时弹掉的边和加入的边,回溯的时候直接逆操作恢复LCT即可,这样复杂度仍然是有保障的;

虽说如此是有保障的,但是实际操作上非常卡!这个算法相当于每一条边加了两次删了两次,再算上LCT的大常数,所以不太好过嘛;

卡卡常数,时间复杂度O(nlog^2n)就可以在BZ过啦;

最终答案要开long long;


代码:


#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 71000
#define M 51000
#define MEM 800000
#define LEN 1<<15
#define lson l,mid,no<<1
#define rson mid+1,r,no<<1|1
#define which(x) (ch[fa[x]][1]==x)
using namespace std;
typedef long long ll;
struct edge
{
	int x,y,val,no;
}E[M],list[MEM];
ll ans[M],tot;
int last[M];
int head[M<<2],next[MEM],ce;
int fa[N],ch[N][2],val[N],ma[N],n,m;
int st[MEM],stv[MEM],top;
bool opt[MEM];
bool rev[N],rt[N];
inline char getc()
{
    static char *S,*T,buf[LEN];
    if(S==T)
    {
        T=(S=buf)+fread(buf,1,LEN,stdin);
        if(S==T)
            return EOF;
    }
    return *S++;
}
inline bool isdigit(char x)
{
	return '0'<=x&&x<='9';
}
inline int read()
{
    static char ch;
    static int D;
    while(!isdigit(ch=getc()));
    for(D=ch-'0';isdigit(ch=getc());)
        D=D*10+ch-'0';
    return D;
}
inline void Pushup(int x)
{
	ma[x]=val[ma[ch[x][0]]]>val[ma[ch[x][1]]]?ma[ch[x][0]]:ma[ch[x][1]];
	ma[x]=val[ma[x]]>val[x]?ma[x]:x;
}
inline void Pushdown(int x)
{
	if(rev[x])
	{
		swap(ch[x][0],ch[x][1]);
		rev[ch[x][0]]^=1;
		rev[ch[x][1]]^=1;
		rev[x]=0;
	}
}
inline void down(int x)
{
	if(!rt[x])	down(fa[x]);
	Pushdown(x);
}
inline void Rotate(int x)
{
	int f=fa[x];
	bool k=which(x);
	ch[f][k]=ch[x][!k];
	ch[x][!k]=f;
	if(rt[f])	rt[f]^=rt[x]^=1;
	else		ch[fa[f]][which(f)]=x;
	fa[ch[f][k]]=f;
	fa[x]=fa[f];
	fa[f]=x;
	Pushup(f);
//	Pushup(x);
}
inline void Splay(int x)
{
	down(x);
	while(!rt[x])
	{
		int f=fa[x];
		if(rt[f])
		{
			Rotate(x);
			break;
		}
		if(which(x)^which(f))
			Rotate(x);
		else
			Rotate(f);
		Rotate(x);
	}
	Pushup(x);
}
inline void access(int x)
{
	int y=0;
	while(x)
	{
		Splay(x);
		rt[ch[x][1]]=1;
		ch[x][1]=y;
		rt[y]=0;
		Pushup(x);
		y=x,x=fa[x];
	}
}
inline void Mtr(int x)
{
	access(x);
	Splay(x);
	swap(ch[x][0],ch[x][1]);
	rev[ch[x][0]]^=1;
	rev[ch[x][1]]^=1;
}
inline bool judge(int x,int y)
{
	Mtr(x);
	access(y);
	Splay(x);
	while(ch[x][1])
		x=ch[x][1];
	return x==y;
}
inline void Cut(int x)
{
	Mtr(x);
	static int t;
	t=E[x-n].x;
	access(t);
	Splay(x);
	rt[t]=1,fa[t]=0;
	ch[x][1]=0;
	Pushup(x);
	t=E[x-n].y;
	access(t);
	Splay(x);
	rt[t]=1,fa[t]=0;
	ch[x][1]=0;
	Pushup(x);
}
inline void Link(int x)
{
	Mtr(x);
	fa[x]=E[x-n].x;
	Mtr(x);
	fa[x]=E[x-n].y;
}
inline void add(int no,edge &x)
{
	list[++ce]=x;
	next[ce]=head[no];
	head[no]=ce;
}
inline void update(int l,int r,int no,int st,int en,edge &x)
{
	if(st<=l&&r<=en)
		add(no,x);
	else
	{
		int mid=l+r>>1;
		if(en<=mid)		update(lson,st,en,x);
		else if(st>mid)	update(rson,st,en,x);
		else	update(lson,st,en,x),update(rson,st,en,x);
	}
}
inline void slove(int l,int r,int no)
{
	int cnt,i,temp;
	for(i=head[no],cnt=0;i;i=next[i])
	{
		temp=0;
		if(!judge(list[i].x,list[i].y)||val[temp=ma[list[i].x]]>=list[i].val)
		{
			if(temp)
			{
				cnt++,top++;
				st[top]=temp;
				stv[top]=val[temp];
				opt[top]=0;
				Cut(temp);
				tot-=val[temp];
			}
			cnt++,top++;
			st[top]=list[i].no;
			opt[top]=1;
			val[list[i].no]=list[i].val;
			Link(list[i].no);
			tot+=list[i].val;
		}
	}
	if(l==r)
	{
		ans[l]=tot;
	}
	else
	{
		int mid=l+r>>1;
		slove(lson);
		slove(rson);
	}
	while(cnt)
	{
		if(opt[top]==0)
		{
			val[st[top]]=stv[top];
			Link(st[top]);
			tot+=val[st[top]];
		}
		else
		{
			Cut(st[top]);
			tot-=val[st[top]];
		}
		top--;
		cnt--;
	}
}
int main()
{
	int q,i,j,k,x,y,v;
	n=read(),m=read(),q=read();
	for(i=1;i<=m;i++)
	{
		E[i].x=read(),E[i].y=read(),E[i].val=read();
		last[i]=1;
		E[i].no=i+n;
	}
	for(i=1;i<=q;i++)
	{
		x=read(),y=read();
		if(last[x]<=i-1)
			update(1,q,1,last[x],i-1,E[x]);
		last[x]=i,E[x].val=y;
	}
	for(i=1;i<=m;i++)
	{
		update(1,q,1,last[i],q,E[i]);
	}
	for(i=1;i<=n+m;i++)
	{
		rt[i]=1;
		ma[i]=i;
	}
	slove(1,q,1);
	for(i=1;i<=q;i++)
	{
		printf("%lld\n",ans[i]);
	}
	return 0;
}



bzoj-2001 City 城市建设

标签:bzoj   分治   线段树   lct   最小生成树   

原文地址:http://blog.csdn.net/ww140142/article/details/49779217

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!