• 简单数据结构题(from 钟子谦——IOI2018集训队自选题)


    简单数据结构题(from 钟子谦——IOI2018集训队自选题)

    试题描述

    给一棵 (n) 个点的树,点权开始为 (0) ,有 (q) 次操作,每次操作是选择一个点,把周围一圈点点权 (+1)(一个点周围的点为与该点距离为 (1) 的点),在该操作后你需要输出当前周围一圈点点权的异或和。

    由于输出量较大,设第 (i) 个询问输出为 (ans_i),你只需要输出

    egin{equation}
    [sum^q_{i=1}ans_i cdot (i^2+i)] exttt{ mod } (10^9+7)
    otag
    end{equation}

    输入

    第一行两个数 (n)(q) ,表示树的点数和操作数。

    接下来 (n-1) 行每行两个数表示树上的一条边。

    接下来 (q) 行每行一个数 (x),表示把 (x) 周围一圈点点权 (+1)

    输出

    输出一个 ([0,10^9+7)) 的数,详见题目描述。

    输入示例

    5 10
    1 2
    2 3
    2 4
    3 5
    1
    5
    2
    4
    3
    5
    4
    2
    3
    1
    

    输出示例

    2060
    

    数据规模及约定

    对于 (80 exttt{%}) 的数据,保证 (n = 1000)

    对于 (90 exttt{%}) 的数据,保证 (n = 100000)

    对于 (100 exttt{%}) 的数据,保证 (n = 500000)

    题解

    这题差最后一步想到了。

    不过好像除了 trie 树这步最妙这题也没其他什么了。。。

    每个点周围的点可以分为父节点和子树中的一层节点,所以我们可以把一个节点 (u) 的所有儿子的信息存储到节点 (u) 中,然后对于一个询问,父亲单独计算,统一计算所有儿子的。

    所以现在要一个可以支持删除、插入(对父亲的单独操作)、全体加 (1)、求全体异或和的数据结构。

    一个数 (x) 加上 (1) 可以看做 (x ightarrow x igoplus (2 cdot lowbit( exttt{~}x) - 1))(igoplus) 表示异或运算符,( exttt{~}) 表示取反运算符,(lowbit(t)) 表示只取 (t) 最低位的 (1)),所以我们需要维护集合内所有 (x) 的同时维护所有的 (lowbit( exttt{~}x)),确切地,只需要知道每种 (lowbit(x))(最多 (log q) 种)的取值有多少个。

    然后就是我没想到的最后一步了:建立 trie 树保存集合内所有数(从浅到深按从低位到高位的顺序保存),(lowbit( exttt{~}x)) 很好得到了,然后全体 (+1) 就是每次交换左右子树,然后递归到左子树去(即最低位异或 (1) 然后处理进位)。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    #define rep(i, s, t) for(int i = (s); i <= (t); i++)
    #define dwn(i, s, t) for(int i = (s); i >= (t); i--)
    
    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 500010
    #define maxm 1000010
    #define maxnode 10000010
    #define maxlog 25
    #define MOD 1000000007
    #define LL long long
    
    int n, m, head[maxn], nxt[maxm], to[maxm], Fa[maxn], at[maxn];
    int ToT, rt[maxn], val[maxnode], ch[maxnode][2], siz[maxnode], fa[maxnode], rec[maxlog], cntr;
    
    void AddEdge(int a, int b) {
    	to[++m] = b; nxt[m] = head[a]; head[a] = m;
    	swap(a, b);
    	to[++m] = b; nxt[m] = head[a]; head[a] = m;
    	return ;
    }
    
    int getnode() {
    	int u;
    	if(cntr) u = rec[cntr--];
    	else u = ++ToT;
    	ch[u][0] = ch[u][1] = fa[u] = siz[u] = 0;
    	return u;
    }
    int Insert(int& r, int v) {
    	if(!r) r = getnode(), val[r] = v;
    	else val[r] ^= v;
    	int u = r; siz[u]++;
    	rep(i, 1, 20) {
    		int x = v & 1; v >>= 1;
    		if(!ch[u][x]) fa[ch[u][x] = getnode()] = u;
    		u = ch[u][x]; siz[u]++;
    	}
    //	printf("Insert(%d): %d
    ", r, u);
    	return u;
    }
    
    void build(int u) {
    	rt[u] = ++ToT;
    	for(int e = head[u]; e; e = nxt[e]) if(to[e] != Fa[u]) {
    		at[to[e]] = Insert(rt[u], 0);
    		Fa[to[e]] = u;
    		build(to[e]);
    	}
    	return ;
    }
    
    int getnum(int u) {
    	int res = 0; cntr = 0;
    	while(u) {
    		if(fa[u] && ch[fa[u]][1] == u) res = res << 1 | 1;
    		else if(fa[u]) res <<= 1;
    //		printf("getnum: %d
    ", u);
    		siz[u]--;
    		if(!siz[u] && fa[u]) {
    			rec[++cntr] = u;
    //			printf("recycle %d
    ", u);
    			if(ch[fa[u]][1] == u) ch[fa[u]][1] = 0;
    			else ch[fa[u]][0] = 0;
    		}
    		if(!fa[u]) val[u] ^= res;
    		u = fa[u];
    	}
    	return res;
    }
    void Add(int r) {
    	int u = r, d = 0;
    	while(u) {
    		int s = ch[u][0] ? siz[ch[u][0]] : 0;
    		if(!ch[u][1]) s = siz[u];
    		if(s & 1) val[r] ^= (1 << d + 1) - 1;
    		u = ch[u][1]; d++;
    	}
    	u = r;
    	while(u) {
    		swap(ch[u][0], ch[u][1]);
    		u = ch[u][0];
    	}
    	return ;
    }
    
    int main() {
    	n = read(); int q = read();
    	rep(i, 1, n - 1) {
    		int a = read(), b = read();
    		AddEdge(a, b);
    	}
    	
    	Insert(rt[0], 0);
    	build(1);
    	int Ans = 0;
    	rep(i, 1, q) {
    		int u = read(), ans = 0;
    		if(Fa[u]) {
    			ans = getnum(at[Fa[u]]) + 1;
    //			printf("faans: %d
    ", ans);
    			at[Fa[u]] = Insert(rt[Fa[Fa[u]]], ans);
    		}
    //		printf("beforeAdd: %d
    ", val[rt[u]]);
    		Add(rt[u]);
    		ans ^= val[rt[u]];
    //		printf("afterAdd: %d
    ", val[rt[u]]);
    		(Ans += (LL)ans * ((LL)i * i % MOD + i) % MOD) %= MOD;
    	}
    	printf("%d
    ", Ans);
    	
    	return 0;
    }
    
  • 相关阅读:
    进度报告(五)
    大道至简读后感
    进度报告(四)
    进度报告(三)
    进度报告(二)
    进度报告(一)
    18.4---2出现了几次(CC150)
    17.5---珠玑妙算,四个槽,红色黄色绿色蓝色(CC150)
    17.2---#字棋(CC150)
    17.3---阶乘尾多少个0(CC150)
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/7834859.html
Copyright © 2020-2023  润新知