• [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 ;
    }
    
  • 相关阅读:
    数组——遍历
    数组常用方法——汇总
    箭头函数
    overflow:auto产生的滚动条在安卓系统下能平滑滚动,而在ios下滚动不平滑
    浅谈过载保护
    tornado中使用torndb,连接数过高的问题
    如何开启并查看博客园MetaWeblog接口的xml-RPC协议访问地址
    aardio陷阱(转)
    aardio获取公网IP地址代码
    sql常用函数学习笔记整理
  • 原文地址:https://www.cnblogs.com/beretty/p/10064764.html
Copyright © 2020-2023  润新知