【点分治】hdu4918

23 篇文章 0 订阅
2 篇文章 0 订阅

用类似qtree4的思想,用点分治将整棵树分层,可以看到,点分治每个点都会作为一次根,那么要求v的答案,首先可以直接用树状数组求出v这个分治子树的答案,然后对于v的上一层的分治祖先u,v~u的距离已知,u这个分治子树的距离等信息也已存在,那么可以类似的求答案,但是要减去v这个子树和u这个子树的交的部分,这个求答案也是类似的,然后再去做u的祖先和u'的信息。

由于至多只有log次祖先因此复杂度是O(n(logn)^2)

要注意的是,不能用vector,空间不会回收的话,多组数据很容易暴空间,可以开个大内存池;v的各级祖先的距离信息不能递推,要每次根据v直接算出,因为对于v,v的祖先u,u的祖先u',v~u'的路径不一定经过u。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <utility>
const int oo=1073741819;
using namespace std;
struct ccmp{
	int dep,e;
}Cmp;
int n,ss,q,w_time;
int tail[200000],next[500000],sora[500000];
int rt[200000],fr[200000],w[200000],rel[200000],v[200000],st[200000];
int size[200000],MaxT[200000];
int vec[7000000],sum[7000000],op[19][200000][2],ed[19][200000][2],s1;
int f[20][200000][2],pos[20][200000][2];
void origin()
{
	ss=n,s1=0;
	for (int i=1;i<=n;i++) tail[i]=i,next[i]=0;
	for (int i=1;i<=n;i++)
		rt[i]=fr[i]=w[i]=rel[i]=v[i]=st[i]=size[i]=MaxT[i]=0;
}
void change(int dep,int s,int e,int x,int w)
{
	int l=op[dep][s][e],r=ed[dep][s][e];
	x-=l;
	for (;x<=r-l+1;x+=(x & -x)) 
		sum[x+l]+=w;		
}
int ask(int dep,int s,int e,int d)
{
	if (d<0) return 0;
	int l=op[dep][s][e]+1,r=ed[dep][s][e];
	for (;l<=r;) {
		int mid=((l+r)>>1);
		if (f[dep][vec[mid]][e]<=d) l=mid+1;
		else r=mid-1;
	}
	int x=l-1;
	{
		int ans=0;
		int l=op[dep][s][e];
		x-=l;
		for (;x;x-=(x & -x))
			ans+=sum[x+l];
		return ans;
	}
}
bool cmp(int i,int j)
{
	return f[Cmp.dep][i][Cmp.e]<f[Cmp.dep][j][Cmp.e];
}
int bfs(int dep,int s,int e)
{
	int h,r,ne,na;
	h=r=0;
	w_time++;
	st[r=1]=s,f[dep][s][e]=0,v[s]=w_time;
	for (;h<r;) {
		ne=st[++h];
		for (int i=ne;next[i];) {
			i=next[i],na=sora[i];
			if (rel[na] || v[na]==w_time) continue;
			v[na]=w_time;
			f[dep][na][e]=f[dep][ne][e]+1;
			st[++r]=na;
		}
	}
	op[dep][s][e]=ed[dep][s][e]=++s1;
	for (int i=1;i<=r;i++) {
		ed[dep][s][e]=++s1;
		vec[s1]=st[i],sum[s1]=0;
	}
	Cmp.dep=dep,Cmp.e=e;
	sort(vec+op[dep][s][e]+1,vec+ed[dep][s][e]+1,cmp);
	for (int i=op[dep][s][e]+1;i<=ed[dep][s][e];i++) {
		pos[dep][vec[i]][e]=i;
		change(dep,s,e,i,w[vec[i]]);
	}
	return r;
}
pair < int , int > dfs(int x,int N)
{	
	pair < int , int > ans;
	ans.first=0;
	ans.second=oo;
	size[x]=1,MaxT[x]=0,v[x]=w_time;
	for (int i=x,ne;next[i];) {
		i=next[i],ne=sora[i];
		if (v[ne]==w_time || rel[ne]) continue;
		pair < int , int > tmp;
		tmp=dfs(ne,N);
		size[x]+=size[ne];
		MaxT[x]=max(MaxT[x],size[ne]);
		if (tmp.second<ans.second) ans=tmp;
	}
	MaxT[x]=max(MaxT[x],N-size[x]);
	if (MaxT[x]<ans.second) {
		ans.first=x;
		ans.second=MaxT[x];
	}
	return ans;
}
void mysoul(int x,int y,int e)
{
	int N=bfs(e,x,0);
	w_time++;
	int root=(dfs(x,N)).first;
	rt[root]=y,fr[root]=x;
	bfs(e,root,1);
	rel[root]=e;
	for (int i=root,ne;next[i];) {
		i=next[i],ne=sora[i];
		if (rel[ne]) continue;
		mysoul(ne,root,e+1);
	}
}
void link(int x,int y)
{
	++ss,next[tail[x]]=ss,tail[x]=ss,sora[ss]=y,next[ss]=0;
	++ss,next[tail[y]]=ss,tail[y]=ss,sora[ss]=x,next[ss]=0;
}
int main()
{
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	for (;scanf("%d%d",&n,&q)==2;) {
		origin();
		for (int i=1;i<=n;i++) scanf("%d",&w[i]);
		for (int i=1;i<=n-1;i++) {
			int x,y;
			scanf("%d%d",&x,&y);
			link(x,y);
		}
		mysoul(1,0,1);
		for (int i=1;i<=q;i++) {
			char ch[5];
			scanf("%s",ch);
			if ('!'==ch[0]) {
				int v,x;
				scanf("%d%d",&v,&x);
				for (int i=v,dep=rel[v];i;i=rt[i],dep--) {
					if (fr[i]) change(dep,fr[i],0,pos[dep][v][0],x-w[v]);
					change(dep,i,1,pos[dep][v][1],x-w[v]);
				}
				w[v]=x;
			}
			else {
				int v,d;
				scanf("%d%d",&v,&d);
				int ans=0;
				for (int i=v,dep=rel[v],dis=0;i;i=rt[i],dep--) {
					dis=f[dep][v][1];
					ans+=ask(dep,i,1,d-dis);
					if (rt[i]) {
						dis=f[dep-1][v][1]+f[dep-1][fr[i]][1];
						ans-=ask(dep,fr[i],0,d-dis);
					}
					dis=f[dep][v][1];
				}
				printf("%d\n",ans);
			}
		}
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值