zoukankan      html  css  js  c++  java
  • [bzoj4372] 烁烁的游戏 [动态点分治+线段树+容斥原理]

    题面

    传送门

    思路

    观察一下题目,要求的是修改“距离点$u$的距离一定的点权值”,那这个就不能用传统的dfs序类算法+线段树维护,因为涉及到向父亲回溯的问题

    看到和树上距离相关的东西,还能想到什么呢?

    没错,点分治算法

    然后发现本题有修改操作,那动态点分治试一试?

    如何点分治?

    我们先把这棵树的点分树构造出来(后面的操作都是在点分树上的了)

    注意到我们一开始的dfs序想法中,影响最大的是往父亲回溯是可以达到$O(n)$的,再操作会炸

    但是点分树的深度是严格$O(log_2n)$的,所以我们要是往上回溯也最多会回溯$log_2n$个节点

    那么这是不是说明我们可以在点分树上开好线段树,然后每次对于当前点和其所有祖先更新信息,这样单次操作时间复杂度是$O(log_2^2n)$的,可以过掉这道题

    线段树上怎么操作?

    确定了线段树之后,我们发现另一个问题,那就是每次往根走的时候,因为修改点到当前枚举的它的祖先是有一个距离的,所以我们在这一点的线段树上维护时只能更新一定深度的点,而且还要去掉之前已经更新过的点......

    好麻烦,而且这个去掉的部分并没有什么办法做qwq

    那么怎么办呢?我们可以考虑换个线段树的定义

    我们之前的所有讨论,都是基于线段树的节点代表点分树上的真实节点(不管是dfs序还是bfs序之类的),但是这道题的一个关键就是距离 ,那么我们可不可以拿距离来定义一棵线段树呢??

    以距离为数组下标的线段树

    我们对于每一个点开一棵动态开点线段树,定义线段树叶节点$l$,其值为$w[l]$,其代表当前节点$u$对$u$的点分树子树中距离为$l$的所有点做出的修改为$w[l]$

    因为本题中所有的修改都是固定距离的,所以对同距离的点的修改可以合并在一起

    那么接下来,我们来处理之前的去重问题

    容易发现,现在的这棵线段树而言,我们只需要对某个点修改的同时,再新开另一棵线段树在这个点的父亲上,把当前点修改的部分记录在上面,询问的时候去掉(因为当前的定义是点分树子树中

    具体而言,对于点u,设当前已经枚举到了它的祖先f1,下一个是f2,修改的权值是w,询问距离范围是d

    那么对于f1,我们需要更新区间$[0,d-dis(u,f1)]$,值加上w

    同时我们在f2的第二棵线段树中,更新区间$[0,d-dis(u,f1)]$,值加上w

    查询的时候,我们依旧从$u$每次往上跳,对于每个祖先$f$,我们在他的两棵线段树上单点查询,每次查询位置$dis(u,f)$,用第一棵线段树的值减去第二棵的

    最后,修改和询问的时候,一开始都要对当前点u处理,因为$u$没有儿子需要斥掉(这一段本质是个容斥原理)

    线段树是区间修改单点查询的动态开点线段树,使用标记永久化可以方便地实现

    如果还有没说清楚的,可以参考一下代码qwq

    Code

    说起来算法还挺复杂的.....但是代码不算长哦

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cassert>
    #include<ctime>
    #define ll long long
    #define log lg2
    using namespace std;
    inline int read(){
    	int re=0,flag=1;char ch=getchar();
    	while(ch>'9'||ch<'0'){
    		if(ch=='-') flag=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
    	return re*flag;
    }
    int n,q,first[100010],cnte,son[100010],siz[100010],fa[100010],dep[100010],app[100010],sum;
    int rt,log[500010],euler[500010],cntu=0,st[500010][20],back[500010];
    struct edge{
    	int to,next;
    }a[200010];
    inline void add(int u,int v){
    	a[++cnte]=(edge){v,first[u]};first[u]=cnte;
    	a[++cnte]=(edge){u,first[v]};first[v]=cnte;
    }
    void getdep(int u,int f){
    	int i,v;dep[u]=dep[f]+1;euler[++cntu]=dep[u];app[u]=cntu;back[cntu]=u;
    	for(i=first[u];~i;i=a[i].next){
    		v=a[i].to;if(v==f) continue;
    		getdep(v,u);
    		euler[++cntu]=dep[u];back[cntu]=u;
    	}
    }
    void ST(){
    	log[1]=0;
    	int i,j,l,r;
    	for(i=2;i<=cntu;i++) log[i]=log[i>>1]+1;
    	for(i=1;i<=cntu;i++) st[i][0]=i;
    	for(j=1;j<=19;j++){
    		for(i=1;i+(1<<j)<=cntu;i++){
    			l=st[i][j-1];r=st[i+(1<<(j-1))][j-1];
    			st[i][j]=(euler[l]<euler[r])?l:r;
    		}
    	}
    }
    int lca(int l,int r){//ST表rmq实现LCA
    	l=app[l];r=app[r];
    	if(l>r) swap(l,r);
    	int k=log[r-l+1],x,y,re;
    	x=st[l][k];y=st[r-(1<<k)+1][k];
    	re=((euler[x]<euler[y])?x:y); 
    	return back[re];
    }
    int getdis(int l,int r){
    	return dep[l]+dep[r]-2*dep[lca(l,r)];
    }
    
    bool vis[100010]={0};
    void getroot(int u,int f){
    	int i,v;siz[u]=1;son[u]=0;
    	for(i=first[u];~i;i=a[i].next){
    		v=a[i].to;if(vis[v]||v==f) continue;
    		getroot(v,u);
    		siz[u]+=siz[v];son[u]=max(son[u],siz[v]);
    	}
    	son[u]=max(son[u],sum-siz[u]);
    	if(son[rt]>son[u]) rt=u;
    }
    void build(int u){//建立点分树
    	int i,v;vis[u]=1;
    	for(i=first[u];~i;i=a[i].next){
    		v=a[i].to;if(vis[v]) continue;
    		rt=0;son[rt]=1e9;sum=siz[v];
    		getroot(v,0);fa[rt]=u;build(rt);
    	}
    }
    
    int root[100010][2],lazy[20000010],cnt,ch[20000010][2];
    void insert(int &cur,int l,int r,int ql,int qr,int val){
    	if(!cur) cur=++cnt;
    	if(l==ql&&r==qr){lazy[cur]+=val;return;}
    	int mid=(l+r)>>1;
    	if(qr<=mid) insert(ch[cur][0],l,mid,ql,qr,val);
    	else{
    		if(ql>mid) insert(ch[cur][1],mid+1,r,ql,qr,val);
    		else{
    			insert(ch[cur][0],l,mid,ql,mid,val);
    			insert(ch[cur][1],mid+1,r,mid+1,qr,val);
    		}
    	}
    }
    void update(int u,int d,int val){
    	int tmp=u,dis;
    	insert(root[u][0],0,n,0,d,val);//先搞当前点
    	for(;fa[u];u=fa[u]){
    		dis=d-getdis(tmp,fa[u]);
    		if(dis<0) continue;//如果距离已经大于询问的距离了,那么显然不用改了
    		insert(root[fa[u]][0],0,n,0,dis,val);//分别更新两棵树
    		insert(root[u][1],0,n,0,dis,val);
    	}
    }
    int query(int cur,int l,int r,int pos){
    	if(!cur) return 0;
    	if(l==r) return lazy[cur];
    	int mid=(l+r)>>1;
    	if(pos<=mid) return lazy[cur]+query(ch[cur][0],l,mid,pos);
    	else return lazy[cur]+query(ch[cur][1],mid+1,r,pos);
    }
    int getans(int u){
    	int re=query(root[u][0],0,n,0),tmp=u;//先加上当前点询问
    	for(;fa[u];u=fa[u]){
    		re+=query(root[fa[u]][0],0,n,getdis(tmp,fa[u]))-query(root[u][1],0,n,getdis(tmp,fa[u]));//两个值相减
    	}
    	return re;
    }
    int main(){
    	memset(first,-1,sizeof(first));
    	n=read();q=read();int i,t1,t2,t3;char s[10];
    	for(i=1;i<n;i++){
    		t1=read();t2=read();
    		add(t1,t2);
    	}
    
    	getdep(1,0);ST();
    	son[rt]=1e9;rt=0;sum=n;
    	getroot(1,0);build(rt);
    	
    	for(i=1;i<=q;i++){
    		scanf("%s",s);
    		if(s[0]=='Q'){
    			t1=read();
    			printf("%d
    ",getans(t1));
    		}
    		else{
    			t1=read();t2=read();t3=read();
    			update(t1,t2,t3);
    		}
    	}
    }
    
  • 相关阅读:
    js 改变颜色值
    React之使用Context跨组件树传递数据
    App.js实现使用js开发app的应用,此文是中文文档
    转: CSS3 @media 用法总结
    转: 如何用手机访问电脑本地 localhost 网页或者服务器, 以调试web项目
    js 替换字符串中所有匹配的字符
    转:display:flex不兼容Android、Safari低版本的解决方案 【flex布局】
    转:HTML5页面如何在手机端浏览器调用相机、相册功能
    文本相似度度量
    idea中maven中jdk版本的选择(转)
  • 原文地址:https://www.cnblogs.com/dedicatus545/p/9368063.html
Copyright © 2011-2022 走看看