簡單數據結構題(from 鍾子謙——IOI2018集訓隊自選題)


簡單數據結構題(from 鍾子謙——IOI2018集訓隊自選題)

試題描述

給一棵 \(n\) 個點的樹,點權開始為 \(0\) ,有 \(q\) 次操作,每次操作是選擇一個點,把周圍一圈點點權 \(+1\)(一個點周圍的點為與該點距離為 \(1\) 的點),在該操作后你需要輸出當前周圍一圈點點權的異或和。

由於輸出量較大,設第 \(i\) 個詢問輸出為 \(ans_i\),你只需要輸出

\begin{equation}
[\sum^q_{i=1}ans_i \cdot (i^2+i)] \texttt{ mod } (10^9+7)
\notag
\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\texttt{%}\) 的數據,保證 \(n = 1000\)

對於 \(90\texttt{%}\) 的數據,保證 \(n = 100000\)

對於 \(100\texttt{%}\) 的數據,保證 \(n = 500000\)

題解

這題差最后一步想到了。

不過好像除了 trie 樹這步最妙這題也沒其他什么了。。。

每個點周圍的點可以分為父節點和子樹中的一層節點,所以我們可以把一個節點 \(u\) 的所有兒子的信息存儲到節點 \(u\) 中,然后對於一個詢問,父親單獨計算,統一計算所有兒子的。

所以現在要一個可以支持刪除、插入(對父親的單獨操作)、全體加 \(1\)、求全體異或和的數據結構。

一個數 \(x\) 加上 \(1\) 可以看做 \(x \rightarrow x \bigoplus (2 \cdot lowbit(\texttt{~}x) - 1)\)\(\bigoplus\) 表示異或運算符,\(\texttt{~}\) 表示取反運算符,\(lowbit(t)\) 表示只取 \(t\) 最低位的 \(1\)),所以我們需要維護集合內所有 \(x\) 的同時維護所有的 \(lowbit(\texttt{~}x)\),確切地,只需要知道每種 \(lowbit(x)\)(最多 \(\log q\) 種)的取值有多少個。

然后就是我沒想到的最后一步了:建立 trie 樹保存集合內所有數(從淺到深按從低位到高位的順序保存),\(lowbit(\texttt{~}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\n", 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\n", u);
		siz[u]--;
		if(!siz[u] && fa[u]) {
			rec[++cntr] = u;
//			printf("recycle %d\n", 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\n", ans);
			at[Fa[u]] = Insert(rt[Fa[Fa[u]]], ans);
		}
//		printf("beforeAdd: %d\n", val[rt[u]]);
		Add(rt[u]);
		ans ^= val[rt[u]];
//		printf("afterAdd: %d\n", val[rt[u]]);
		(Ans += (LL)ans * ((LL)i * i % MOD + i) % MOD) %= MOD;
	}
	printf("%d\n", Ans);
	
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM