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

bzoj-3052 糖果公园

时间:2015-08-09 14:08:11      阅读:116      评论:0      收藏:0      [点我收藏+]

标签:bzoj   莫队算法   块状树   

题意:

给出一颗n个结点的树,每个结点上有一种糖果∈[1,m];

一个人经过这个结点品尝糖果j获得的愉悦度为w[time[j]]*val[j]  (其中time[j]指j的品尝次数);

给出q次操作,操作有两种:
1:更改某结点的糖果种类;

2:查询某两个结点路径上的愉悦度总和;


题解:

250s的神题,orz各位神犇;

将树分块,每块n^2/3大小,分成n^1/3块;

用莫队算法统计路径上每种糖果的数量;

但是这里有了修改,不能用原来的方式排序了;

所以以左端点所在块为第一关键字,右端点所在块为第二关键字,时间序为第三关键字为所有询问排序;

注意是对询问排序,而修改仍按时间序存储;

然后一一处理询问,每次更改端点之前先把时间调至某次修改之后;

每次调整都是O(1)的,答案就是每个糖果的val乘上w的前缀和了;

这样时间复杂度最坏在O(n^5/3)  (LCA的log似乎被忽视了);

我的代码可以跑进100s真是荣幸啊233



代码:


#include<math.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 110000
using namespace std;
typedef long long ll;
struct node
{
	int l,r,tim,posl,posr,no;
}Q[N];
int to[N<<1],next[N<<1],head[N];
int st[N],belong[N],deep[N],fa[N][20];
int a[N],s[N];
int opx[N],opf[N],opt[N],last[N];
int cnt,tot,bk,top;
ll v[N],w[N],ans[N];
ll now;
bool vis[N];
bool cmp(node a,node b)
{
	if(a.posl==b.posl)
	{
		if(a.posr==b.posr)
			return a.tim<b.tim;
		return a.posr<b.posr;
	}
	return a.posl<b.posl;
}
void add(int x,int y)
{
	to[++cnt]=y;
	next[cnt]=head[x];
	head[x]=cnt;
}
void dfs(int x,int pre,int d)
{
	deep[x]=d;
	fa[x][0]=pre;
	int i,y,b=top;
	for(i=head[x];i;i=next[i])
	{
		if((y=to[i])!=pre)
		{
			dfs(y,x,d+1);
			if(top-b>=bk)
			{
				tot++;
				while(top>b)
					belong[st[top--]]=tot;
			}
		}
	}
	st[++top]=x;
}
int Lca(int x,int y)
{
	while(deep[x]!=deep[y])
	{
		if(deep[x]<deep[y])
			swap(x,y);
		int k=log2(deep[x]-deep[y]);
		x=fa[x][k];
	}
	if(x==y)	return x;
	int k=log2(deep[x])+1;
	while(k>=0)
	{
		if(fa[x][k]!=fa[y][k])
			x=fa[x][k],y=fa[y][k];
		k--;
	}
	return fa[x][0];
}
void update(int x,int op)
{
	now-=w[s[x]]*v[x];
	s[x]+=op;
	now+=w[s[x]]*v[x];
}
void change(int t)
{
	if(a[opx[t]]==opf[t])
	{
		a[opx[t]]=opt[t];
		if(vis[opx[t]])
		{
			update(opf[t],-1);
			update(opt[t],1);
		}
	}
	else
	{
		a[opx[t]]=opf[t];
		if(vis[opx[t]])
		{
			update(opt[t],-1);
			update(opf[t],1);
		}
	}
}
void slove(int x1,int x2)
{
	int g=Lca(x1,x2);
	while(x1!=g)
	{
		update(a[x1],vis[x1]?-1:1);
		vis[x1]=!vis[x1];
		x1=fa[x1][0];
	}
	while(x2!=g)
	{
		update(a[x2],vis[x2]?-1:1);
		vis[x2]=!vis[x2];
		x2=fa[x2][0];
	}
}
int main()
{
	int n,m,q,i,j,k,l,r,x,y,len,op,temp,ti;
	scanf("%d%d%d",&n,&m,&q);
	bk=pow(n,2.0/3);
	for(i=1;i<=m;i++)
		scanf("%lld",v+i);
	for(i=1;i<=n;i++)
		scanf("%lld",w+i),w[i]=w[i-1]+w[i];
	for(i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y),add(y,x);
	}
	for(i=1;i<=n;i++)
		scanf("%d",a+i),last[i]=a[i];
	dfs(1,0,0);
	while(top)
		belong[st[top--]]=tot;
	for(k=1;(1<<k)<=n;k++)
		for(i=1;i<=n;i++)
			fa[i][k]=fa[fa[i][k-1]][k-1];
	for(i=1,temp=len=0;i<=q;i++)
	{
		scanf("%d%d%d",&op,&x,&y);
		if(op)
		{
			temp++;
			if(belong[x]>belong[y])
				swap(x,y);
			Q[temp].l=x,Q[temp].r=y;
			Q[temp].posl=belong[x];
			Q[temp].posr=belong[y];
			Q[temp].tim=len,Q[temp].no=temp;
		}
		else
		{
			len++;
			opx[len]=x,opf[len]=last[x],opt[len]=y;
			last[x]=y;
		}
	}
	q=temp;
	sort(Q+1,Q+q+1,cmp);
	l=1,r=1,now=0,ti=0;
	for(i=1;i<=q;i++)
	{
		while(ti<Q[i].tim)	change(++ti);
		while(ti>Q[i].tim)	change(ti--);
		slove(l,Q[i].l);
		slove(r,Q[i].r);
		l=Q[i].l,r=Q[i].r;
		x=Lca(l,r);
		update(a[x],1);
		ans[Q[i].no]=now;
		update(a[x],-1);
	}
	for(i=1;i<=q;i++)
		printf("%lld\n",ans[i]);
	return 0;
}




bzoj-3052 糖果公园

标签:bzoj   莫队算法   块状树   

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

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