zoukankan      html  css  js  c++  java
  • [BZOJ3730]震波

    Description

    在一片土地上有N个城市,通过N-1条无向边互相连接,形成一棵树的结构,相邻两个城市的距离为1,其中第i个城市的价值为value[i]。
    不幸的是,这片土地常常发生地震,并且随着时代的发展,城市的价值也往往会发生变动。
    接下来你需要在线处理M次操作:
    0 x k 表示发生了一次地震,震中城市为x,影响范围为k,所有与x距离不超过k的城市都将受到影响,该次地震造成的经济损失为所有受影响城市的价值和。
    1 x y 表示第x个城市的价值变成了y。
    为了体现程序的在线性,操作中的x、y、k都需要异或你程序上一次的输出来解密,如果之前没有输出,则默认上一次的输出为0。

    Input

    第一行包含两个正整数N和M。
    第二行包含N个正整数,第i个数表示value[i]。
    接下来N-1行,每行包含两个正整数u、v,表示u和v之间有一条无向边。
    接下来M行,每行包含三个数,表示M次操作。

    Output

    包含若干行,对于每个询问输出一行一个正整数表示答案。

    Sample Input

    8 1
    1 10 100 1000 10000 100000 1000000 10000000
    1 2
    1 3
    2 4
    2 5
    3 6
    3 7
    3 8
    0 3 1

    Sample Output

    11100101

    HINT

    1<=N,M<=100000

    1<=u,v,x<=N

    1<=value[i],y<=10000

    0<=k<=N-1


    题解

    点分树+线段树

    刚刚学习的点分树

    发现是一个神的数据结构

    点分树就是把树重构,使原树变成一个树高为log的东西

    这样在上面暴跳(fa[])之类的操作就变得合理了

    这道题要求我们支持修改点权和查询距离一个点不超过k的点权和

    可以对原树建点分树

    然后开线段树维护相关信息

    这类问题有个套路

    就是因为我们查询和修改时要暴跳(fa[])

    所以在统计(fa[])的信息时需要容斥掉当前子树内的相关信息

    这样我们对于每个点就要开两棵线段树

    一棵线段树rt1表示点u的子树内到u距离小于等于k的点的权值和

    另外一棵线段树rt2表示点u的子树内到(fa[u])的距离小于等于k的点权值和

    这样我们暴跳统计信息的时候就可以直接用(rt1[fa_u] - rt2[u])来容斥掉该子树内的信息了

    然后还有这题卡常然而我常数肥肠大就煤过==

    代码

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    # define ls t[now].l
    # define rs t[now].r
    const int M = 100005 ;
    const int INF = 1e9 + 7 ;
    using namespace std ;
    inline int read() {
    	char c = getchar() ; int x = 0 , w = 1 ;
    	while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
    	while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
    	return x*w ;
    }
    
    bool vis[M] ;
    int n , num , hea[M] ;
    int dep[M] , st[M][20] , val[M] ;
    int tot , tmin = INF , root , size[M] ;
    int cnt , fa[M] , rt1[M] , rt2[M] ;
    struct E {
    	int Nxt , to ;
    } edge[M << 1] ;
    struct Node {
    	int l , r , Sum ;
    } t[M << 7] ;
    inline void add_edge(int from , int to) {
    	edge[++num].Nxt = hea[from] ;
    	edge[num].to = to ; hea[from] = num ;
    }
    
    void Dfs(int u , int father) {
    	st[u][0] = father ;
    	for(int i = hea[u] ; i ; i = edge[i].Nxt) {
    		int v = edge[i].to ; if(vis[v] || v == father) continue ;
    		dep[v] = dep[u] + 1 ; Dfs(v , u) ; 
    	}
    }
    inline void ST() {
    	for(int j = 1 ; j <= 17 ; j ++)
    	    for(int i = 1 ; i <= n ; i ++)
    	        st[i][j] = st[st[i][j - 1]][j - 1] ;
    }
    inline int Gdis(int u , int v) {
    	int ret = 0 ; if(dep[u] < dep[v]) swap(u , v) ;
    	for(int i = 17 ; i >= 0 ; i --)
    	    if(dep[st[u][i]] >= dep[v]) {
    	    	ret += dep[u] - dep[st[u][i]] ;
    	    	u = st[u][i] ;
    		}
    	if(u == v) return ret ;
    	for(int i = 17 ; i >= 0 ; i --) 
    	    if(st[u][i] != st[v][i]) {
    	    	ret += (dep[u] - dep[st[u][i]]) + (dep[v] - dep[st[v][i]]) ;
    	    	u = st[u][i] , v = st[v][i] ;
    		}
    	return ret + (dep[u] - dep[st[u][0]]) + (dep[v] - dep[st[v][0]]) ;
    }
    void Getroot(int u , int father) {
    	size[u] = 1 ; int Mx = -1 ;
    	for(int i = hea[u] ; i ; i = edge[i].Nxt) {
    		int v = edge[i].to ; if(v == father || vis[v]) continue ;
    		Getroot(v , u) ; size[u] += size[v] ; Mx = max(Mx , size[v]) ;
    	}
    	Mx = max(Mx , tot - size[u]) ; if(Mx < tmin) tmin = Mx , root = u ;
    }
    void Solve(int u , int father) {
    	fa[u] = father ; vis[u] = true ;
    	for(int i = hea[u] ; i ; i = edge[i].Nxt) {
    		int v = edge[i].to ; if(vis[v]) continue ;
    		tot = size[v] ; tmin = INF ;
    		Getroot(v , u) ; Solve(root , u) ;
    	}
    }
    inline void pushup(int now) {
    	t[now].Sum = t[ls].Sum + t[rs].Sum ;
    }
    void Insert(int x , int v , int l , int r , int &now) {
    	if(!now) now = ++cnt ;
    	if(l == r) { t[now].Sum += v ; return ; }
    	int mid = (l + r) >> 1 ;
    	if(mid >= x) Insert(x , v , l , mid , ls) ;
    	else Insert(x , v , mid + 1 , r , rs) ;
    	pushup(now) ;
    }
    int query(int L , int R , int l , int r , int now) {
    	if(l > R || r < L) return 0 ;
    	if(!now) return 0 ; if(l == L && r == R) return t[now].Sum ;
    	int mid = (l + r) >> 1 ;
    	if(mid >= R) return query(L , R , l , mid , ls) ;
    	else if(mid < L) return query(L , R , mid + 1 , r , rs) ;
    	else return query(L , mid , l , mid , ls) + query(mid + 1 , R , mid + 1 , r , rs) ;
    }
    void Change(int x,int v) {
    	Insert(0 , v , 0 , n , rt1[x]) ;
    	for(int u = x , dis1 ; fa[u] ; u = fa[u]) {
    		dis1 = Gdis(x , fa[u]) ;
    		Insert(dis1 , v , 0 , n , rt1[fa[u]]) ;
    	    Insert(dis1 , v , 0 , n , rt2[u]) ;
    	}
    }
    int main() {
    	n = read() ; int Q = read() ;
    	for(int i = 1 ; i <= n ; i ++) val[i] = read() ;
    	for(int i = 1 , u , v ; i < n ; i ++) {
    		u = read() , v = read() ;
    		add_edge(u , v) ; add_edge(v , u) ;
    	}
    	Dfs(1 , 0) ; ST() ;
    	tot = n ; Getroot(1 , 1) ; Solve(root , 0) ;
    	for(int x = 1 ; x <= n ; x ++) Change(x,val[x]);
    	int opt , x , k , Ans = 0 , dlt ;
    	while(Q --) {
    		opt = read() , x = read() , k = read() ; x ^= Ans ; k ^= Ans ;
    		if(opt == 1) {
    			dlt = k - val[x] ;
    			Change(x , dlt) ; val[x] = k ;
     		}
    		else {
    			Ans = query(0 , k , 0 , n , rt1[x]) ;
    			for(int i = x , dis1 ; fa[i] ; i = fa[i]) {
    				dis1 = Gdis(x , fa[i]) ;
    				if(k < dis1) continue ;
    				Ans += query(0 , k - dis1 , 0 , n , rt1[fa[i]]) ;
    				Ans -= query(0 , k - dis1 , 0 , n , rt2[i]) ;
    			}
    			printf("%d
    ",Ans) ;
    		}
    	}
    	return 0 ;
    }
    
  • 相关阅读:
    三数之和
    罗马数字与整数
    Oracle 开启或关闭归档
    Oracle RMAN scripts to delete archivelog
    Oracle check TBS usage
    Oracle kill locked sessions
    场景9 深入RAC运行原理
    场景7 Data Guard
    场景4 Data Warehouse Management 数据仓库
    场景5 Performance Management
  • 原文地址:https://www.cnblogs.com/beretty/p/10064764.html
Copyright © 2011-2022 走看看