zoukankan      html  css  js  c++  java
  • [OJ#63]树句节够提

    [OJ#63]树句节够提

    试题描述

    给定一棵节点数为 N 的有根树,其中 1 号点是根节点,除此之外第 i 个节点的父亲为 fi。每个节点有一个权值 Ai,所有边权均为 1

    给定 Q 个询问,每个询问以一个二元组 (x,k) 的形式给出,表示询问以 x 为根的子树内,与 x 距离至少为 k 的所有节点权值之和。

    由于输出量可能过大,我们使用以下方式减少输出量。

    void print(int q, long long* ans, int lim) {
        for(int i = 1; i <= q; ) {
            long long res = 0;
            for(int j = i; j <= min(q, i + lim - 1); j++) res ^= ans[j];
            i += lim;
            printf("%lld
    ", res);
        } 
    }

    程序中 ansi 表示第 i 次询问的答案,你需要在你的程序末尾调用以上函数来输出答案。

    输入

    第一行为一个正整数 N

    第二行为 N 个正整数 Ai

    第三行为 N1 个正整数 fi,第 i 个正整数表示 i+1 的父亲节点。

    第四行为一个正整数 Q

    接下来 Q 行每行两个整数 xk

    最后一行为一个正整数 lim

    输出

    在你的程序末尾调用以上函数来输出答案。

    输入示例

    7
    1 2 3 4 5 6 7
    1 1 2 2 3 3
    5
    2 0
    2 1
    6 1
    6 0
    1 1
    1

    输出示例

    11
    9
    0
    6
    27

    数据规模及约定

    对于 20% 的数据,1N,Q2501

    对于 70% 的数据,1N,Q252501

    对于 100% 的数据,1N,Q25250101Ai525011fii11x,k+1,limN

    保证单个输出文件大小不超过 0.5MB,但如果你需要使用Hack功能,请保证Max(1,floor(N/10000))lim

    题解

    注意:此题标算 O(n)。

    长链剖分“新”用法?(就好像之前写过长链剖分的题一样。。。)

    先离线,将所有询问放入树上对应节点,然后给树进行长链剖分(其实长链剖分主要目的是将“长链”放到区间的连续一段)。然后在处理询问时,因为对于一个子树 x,深度一样的节点地位一样,所以可以将子树的信息全都“压缩”到长链上,由于长链是这个子树 x 中一端在 x 上的最长链,所以能够保证所有的节点都有地方存。

    由于一条长链对应一段连续区间,在询问某个深度的时候就可以 O(1) 用数组查询了。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 2525020
    #define LL long long
    
    int n, q, fa[maxn], m, head[maxn], nxt[maxn], to[maxn], A[maxn];
    
    void AddEdge(int a, int b) {
    	to[++m] = b; nxt[m] = head[a]; head[a] = m;
    	return ;
    }
    
    int dep[maxn], mxd[maxn], son[maxn], pos[maxn], clo;
    void build(int u) {
    	mxd[u] = dep[u];
    	for(int e = head[u]; e; e = nxt[e]) {
    		dep[to[e]] = dep[u] + 1;
    		build(to[e]);
    		mxd[u] = max(mxd[u], mxd[to[e]]);
    		if(!son[u] || mxd[to[e]] > mxd[son[u]]) son[u] = to[e];
    	}
    	return ;
    }
    void gett(int u) {
    	pos[u] = ++clo;
    	if(son[u]) gett(son[u]);
    	for(int e = head[u]; e; e = nxt[e]) if(to[e] != son[u]) gett(to[e]);
    	return ;
    }
    
    struct Que {
    	int head[maxn], nxt[maxn], dep[maxn];
    	Que() { memset(head, 0, sizeof(head)); }
    	void Insert(int u, int d, int id) {
    		dep[id] = d; nxt[id] = head[u]; head[u] = id;
    		return ;
    	}
    } que;
    LL sum[maxn], Ans[maxn];
    void solve(int u) {
    	if(son[u]) solve(son[u]);
    	for(int e = head[u]; e; e = nxt[e]) if(to[e] != son[u]) {
    		solve(to[e]);
    		for(int i = 0; i <= mxd[to[e]] - dep[to[e]]; i++)
    			sum[pos[u]+1+i] += sum[pos[to[e]]+i];
    	}
    	sum[pos[u]] = (son[u] ? sum[pos[son[u]]] : 0) + A[u];
    	for(int e = que.head[u]; e; e = que.nxt[e])
    		Ans[e] = (que.dep[e] <= mxd[u] - dep[u]) ? sum[pos[u]+que.dep[e]] : 0;
    	return ;
    }
    
    void print(int q, int lim) {
        for(int i = 1; i <= q; ) {
            long long res = 0;
            for(int j = i; j <= min(q, i + lim - 1); j++) res ^= Ans[j];
            i += lim;
            printf("%lld
    ", res);
        }
        return ;
    }
    
    int main() {
    	n = read();
    	for(int i = 1; i <= n; i++) A[i] = read();
    	for(int i = 2; i <= n; i++) {
    		fa[i] = read();
    		AddEdge(fa[i], i);
    	}
    	
    	build(1);
    	gett(1);
    	
    	q = read();
    	for(int i = 1; i <= q; i++) {
    		int x = read(), d = read();
    		que.Insert(x, d, i);
    	}
    	solve(1);
    	
    	print(q, read());
    	
    	return 0;
    }
    

    然而这题我写的 O(nlogn) 的算法比上面的 O(n) 要快 233333

    注意不能直接强上主席树,因为空间不够。

    考虑到每个询问就是问子树内深度大于等于某个值(k+dep[x],k 表示该询问的参数,dep[x] 表示该询问中节点 x 的深度,不妨称每个询问的 k+dep[x] 为它的绝对深度)的所有的点权和,所以我们将询问按它们的绝对深度从大到小排序,然后依次处理。那么问题变成每次添加一个点,然后询问区间和,可以用树状数组很快地实现。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    
    const int BufferSize = 1 << 16;
    char buffer[BufferSize], *Head, *Tail;
    inline char Getchar() {
    	if(Head == Tail) {
    		int l = fread(buffer, 1, BufferSize, stdin);
    		Tail = (Head = buffer) + l;
    	}
    	return *Head++;
    }
    int read() {
    	int x = 0, f = 1; char c = Getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
    	return x * f;
    }
    
    #define maxn 2525020
    #define LL long long
    
    int n, q, fa[maxn], m, head[maxn], nxt[maxn], to[maxn], A[maxn];
    
    void AddEdge(int a, int b) {
    	to[++m] = b; nxt[m] = head[a]; head[a] = m;
    	return ;
    }
    
    struct Level {
    	int head[maxn], nxt[maxn];
    	Level() { memset(head, 0, sizeof(head)); }
    	void Insert(int u, int level) {
    		nxt[u] = head[level];
    		head[level] = u;
    		return ;
    	}
    } lev;
    int dep[maxn], dl[maxn], dr[maxn], clo;
    void build(int u) {
    	dl[u] = ++clo;
    	lev.Insert(u, dep[u]);
    	for(int e = head[u]; e; e = nxt[e]) {
    		dep[to[e]] = dep[u] + 1;
    		build(to[e]);
    	}
    	dr[u] = clo;
    	return ;
    }
    
    struct Que {
    	int u, d, id;
    	Que() {}
    	Que(int _1, int _2, int _3): u(_1), d(_2 + dep[u]), id(_3) {}
    	bool operator < (const Que& t) const { return d < t.d; }
    } qs[maxn];
    
    LL C[maxn];
    void add(int x, int v) {
    	for(; x <= n; x += x & -x) C[x] += v;
    	return ;
    }
    LL que(int x) {
    	LL sum = 0;
    	for(; x; x -= x & -x) sum += C[x];
    	return sum;
    }
    
    LL Ans[maxn];
    
    int num[100], cntn;
    void putnum(LL x) {
    	cntn = 0;
    	while(x) num[cntn++] = x % 10, x /= 10;
    	for(int i = cntn - 1; i >= 0; i--) putchar(num[i] + '0');
    	if(!cntn) putchar('0');
    	putchar('
    ');
    	return ;
    }
    
    void print(int q, int lim) {
        for(int i = 1; i <= q; ) {
            long long res = 0;
            for(int j = i; j <= min(q, i + lim - 1); j++) res ^= Ans[j];
            i += lim;
            putnum(res);
        }
        return ;
    }
    
    int main() {
    	n = read();
    	for(int i = 1; i <= n; i++) A[i] = read();
    	for(int i = 2; i <= n; i++) {
    		fa[i] = read();
    		AddEdge(fa[i], i);
    	}
    	
    	build(1);
    	q = read();
    	for(int i = 1; i <= q; i++) {
    		int x = read(), k = read();
    		qs[i] = Que(x, k, i);
    	}
    	sort(qs + 1, qs + q + 1);
    	for(int i = q, j = n; i; i--) {
    		while(j >= qs[i].d) {
    			for(int e = lev.head[j]; e; e = lev.nxt[e]) add(dl[e], A[e]);
    			j--;
    		}
    		Ans[qs[i].id] = que(dr[qs[i].u]) - que(dl[qs[i].u] - 1);
    	}
    	
    	print(q, read());
    	
    	return 0;
    }
    

    此题略丧病。。。

  • 相关阅读:
    python's twenty eithth day for me 模块和包
    python's twenty-seventh day for me 模
    python's twenty-sixth day for me 模块
    python's twenty-fifth day for me 模块
    python's twenty_fourth day for me 内置方法
    python's twenty-third day for me 面向对象进阶
    python's twenty-second day for me 封装,property方法
    python's twenty-first day for me 抽象类和接口类以及多态
    字典
    元组(tuple)基本操作
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/7528690.html
Copyright © 2011-2022 走看看