[BZOJ3351][[IOI2009]Regions][分块]
题目大意:
给定一棵N<=200000个节点的带颜色的有根树,然后给出Q<=200000个询问,对于每个询问(a,b),求所有颜色为a的子树中颜色
思路:
这道题在某次省选模拟赛中出过,然而当时并不会做,打了树剖+前缀和,可以过N,M<=2000的部分分,现在看到这道题的原题,于是对着学长的博客学习了一发。
对于每个询问(a,b),按照颜色b在树上出现次数分类讨论, 大于
对于后者,我们暴力对每个为b的点询问一遍,每种颜色最多出现
N−−√ 次,总共有q个询问,复杂度为O(qN−−√) 而对于前者,这样的颜色最多有N−−√种,我们暴力对每个为a的点询问一遍,最多有
N 个a, 每个a 上询问N−−√个b,复杂度为O(NN−−√)
总复杂度为:O((N+q)N−−√)
代码:
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> Abcd;
typedef long long ll;
const int Maxn = 200005;
namespace IO {
inline char get(void) {
static char buf[1000000], *p1 = buf, *p2 = buf;
if (p1 == p2) {
p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin);
if (p1 == p2) return EOF;
}
return *p1++;
}
inline void read(int &x) {
x = 0; static char c;
for (; !(c >= '0' && c <= '9'); c = get());
for (; c >= '0' && c <= '9'; x = x * 10 + c - '0', c = get());
}
inline void write(ll x) {
if (!x) return (void)puts("0");
if (x < 0) putchar('-'), x = -x;
static short s[12], t;
while (x) s[++t] = x % 10, x /= 10;
while (t) putchar('0' + s[t--]);
putchar('\n');
}
};
int head[Maxn], sub;
struct Edge {
int to, nxt;
Edge(void) {}
Edge(const int &to, const int &nxt) : to(to), nxt(nxt) {}
} edge[Maxn];
inline void add(int a, int b) {
edge[++sub] = Edge(b, head[a]), head[a] = sub;
}
int n, R, Q, v[Maxn], Block, num[Maxn], fact[Maxn];
ll ans[Maxn], tmp[Maxn];
map<Abcd, int> cur;
vector<Abcd> s[Maxn], f[Maxn];
inline void dfs1(int u) {
int t = v[u];
tmp[t]++;
for (int i = 0; i < (int)s[t].size(); i++)
ans[s[t][i].first] += tmp[s[t][i].second];
for (int i = head[u], v; i; i = edge[i].nxt) {
v = edge[i].to;
dfs1(v);
}
tmp[t]--;
}
inline void dfs2(int u) {
int t = v[u];
for (int i = 0; i < (int)f[t].size(); i++)
ans[f[t][i].first] -= tmp[f[t][i].second];
tmp[t]++;
for (int i = head[u], v; i; i = edge[i].nxt) {
v = edge[i].to;
dfs2(v);
}
for (int i = 0; i < (int)f[t].size(); i++)
ans[f[t][i].first] += tmp[f[t][i].second];
}
int main(void) {
//freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
IO::read(n), IO::read(R), IO::read(Q);
IO::read(v[1]);
for (int i = 2; i <= n; i++) {
int fa;
IO::read(fa), IO::read(v[i]);
add(fa, i);
}
Block = sqrt(n) + 1;
for (int i = 1; i <= n; i++) num[v[i]]++;
for (int i = 1; i <= Q; i++) {
int a, b;
IO::read(a), IO::read(b); fact[i] = i;
if (cur.count(Abcd(a, b))) fact[i] = cur[Abcd(a, b)];
else {
cur[Abcd(a, b)] = i;
if (num[b] > Block) f[a].push_back(Abcd(i, b));
else s[b].push_back(Abcd(i, a));
}
}
dfs1(1), dfs2(1);
for (int i = 1; i <= Q; i++)
IO::write(ans[fact[i]]);
return 0;
}
完。
By g1n0st