(dfs序+莫队算法/启发式合并/树分治)Codeforces 375D - Tree and Queries
题意:
一颗根为1的树,每个节点有一个颜色,现在有十万次查询,问x的子树中数量超过k的颜色个数有几个。
分析:
一开始想了一下,朦朦胧胧的觉得dfs一下应该可以做,但是也只是朦朦胧胧,并不知道怎么下手。
百度了一下,“好题中的好题”,4解。
对于前两种方法,依赖于dfs序。
具体的可以自行百度,非常巧妙的东西。
至于莫队,首先莫队是个离线算法,用分块搞一下。
对所有查询排个序,然后不断移动两个指针维护信息即可,证明貌似是用曼哈顿距离生成树。有兴趣可以看看。
其实,整个过程很有滑动窗口的味道, 安排好所有查询的顺序,从左往右滑,不够就补信息,多了就删信息,虽然肯定会有浪费很多信息,不过根号复杂度也无所谓了。
后两种做法其实就是暴力的优化。
对于暴力,我们很自然的想法是每个节点存一个map,记录此子树的所有颜色个数,父亲的map由儿子的map合并而成。
至于维护答案,用一个可以查询Rank的平衡树即可,每次产生一个新的map值,就删掉存在Treap中旧的数量,然后添加新的。
对于启发式合并,上面讲的还不是关键,最关键的是“启发式”这三个字,即每次把小的map并到大的map中。
否则复杂度无法到达nlogn,又由于Treap的内容在跟随变化,整体复杂度为nlognlogn
最后一种方法,是最巧妙的,但正如上面说的,其实就是最暴力的优化。
我们将询问按x分类,对于每个节点进行暴力,那么最坏情况的复杂度显然是n^2。
然而其实,我们其实可以将子树的一部分信息保留到上一层父亲,最优秀的必然是将最大的子树保留给父亲。
所以利用dfs回溯的性质,对于每个节点,都会有1棵最大的子树不需要计算。
整体复杂度为nlogn,虽然我不会证明,不过凭感觉应该差不多。
代码:
dfs序+莫队算法:
1 #include <set> 2 #include <map> 3 #include <list> 4 #include <cmath> 5 #include <queue> 6 #include <vector> 7 #include <bitset> 8 #include <string> 9 #include <cctype> 10 #include <cstdio> 11 #include <cstring> 12 #include <cstdlib> 13 #include <iostream> 14 #include <algorithm> 15 16 using namespace std; 17 18 typedef long long ll; 19 typedef unsigned long long ull; 20 typedef pair<int, int> pii; 21 #define inf (0x3f3f3f3f) 22 #define lnf (0x3f3f3f3f3f3f3f3f) 23 #define eps (1e-6) 24 #define fi first 25 #define se second 26 #define ls (n<<1) 27 #define rs (n<<1|1) 28 int sgn(double a) { 29 return a < -eps ? -1 : a < eps ? 0 : 1; 30 } 31 32 //-------------------------- 33 34 const int maxn = 100010; 35 const int mod = 1e9 + 7; 36 37 38 int n, q; 39 int col[maxn]; 40 int L[maxn]; 41 int R[maxn]; 42 int d[maxn]; 43 int tot = 0; 44 vector<int> g[maxn]; 45 struct Q { 46 int l, r, k, index; 47 } query[maxn]; 48 int ans[maxn]; 49 int suf[maxn]; 50 int color[maxn]; 51 int cs[maxn]; 52 int bks; 53 54 bool cmp(Q a, Q b) { 55 if (a.l / bks == b.l / bks)return a.r < b.r; 56 else return a.l / bks < b.l / bks; 57 } 58 59 void dfs(int u, int fa, int dep) { 60 L[u] = ++tot; 61 color[tot] = col[u]; 62 d[u] = dep; 63 for (int i = 0; i < g[u].size(); i++) { 64 int v = g[u][i]; 65 if (v == fa)continue; 66 dfs(v, u, dep + 1); 67 } 68 R[u] = tot; 69 } 70 71 void solve() { 72 scanf("%d%d", &n, &q); 73 bks = (int)sqrt(n); 74 for (int i = 1; i <= n; i++) { 75 scanf("%d", &col[i]); 76 } 77 for (int i = 0; i < n - 1; i++) { 78 int u, v; 79 scanf("%d%d", &u, &v); 80 g[u].push_back(v); 81 g[v].push_back(u); 82 } 83 dfs(1, -1, 0); 84 int v; 85 for (int i = 0; i < q; i++) { 86 scanf("%d%d", &v, &query[i].k); 87 query[i].l = L[v]; 88 query[i].r = R[v]; 89 query[i].index = i; 90 } 91 sort(query, query + q, cmp); 92 int pl = 1, pr = 0; 93 for (int i = 0; i < q; i++) { 94 int index = query[i].index; 95 if (query[i].l == query[i].r) { 96 ans[index] = query[i].k > 1 ? 0 : 1; 97 continue; 98 } 99 if (pr < query[i].r) { 100 for (int j = pr + 1; j <= query[i].r; j++) { 101 suf[++cs[color[j]]]++; 102 } 103 104 } else { 105 for (int j = pr; j > query[i].r; j--) { 106 suf[cs[color[j]]--]--; 107 } 108 } 109 pr = query[i].r; 110 if (pl < query[i].l) { 111 for (int j = pl; j < query[i].l; j++) { 112 suf[cs[color[j]]--]--; 113 } 114 } else { 115 for (int j = pl - 1; j >= query[i].l; j--) { 116 suf[++cs[color[j]]]++; 117 } 118 } 119 pl = query[i].l; 120 ans[index] = suf[query[i].k]; 121 } 122 for (int i = 0; i < q; i++) { 123 printf("%d\n", ans[i] ); 124 } 125 } 126 127 128 int main() { 129 130 #ifndef ONLINE_JUDGE 131 freopen("1.in", "r", stdin); 132 // freopen("1.out", "w", stdout); 133 #endif 134 //iostream::sync_with_stdio(false); 135 solve(); 136 return 0; 137 }
启发式合并:
1 #include <set> 2 #include <map> 3 #include <list> 4 #include <cmath> 5 #include <queue> 6 #include <vector> 7 #include <bitset> 8 #include <string> 9 #include <cctype> 10 #include <cstdio> 11 #include <cstring> 12 #include <cstdlib> 13 #include <iostream> 14 #include <algorithm> 15 #include <unordered_map> 16 17 using namespace std; 18 19 typedef long long ll; 20 typedef unsigned long long ull; 21 typedef pair<int, int> pii; 22 #define inf (0x3f3f3f3f) 23 #define lnf (0x3f3f3f3f3f3f3f3f) 24 #define eps (1e-6) 25 #define fi first 26 #define se second 27 #define ls (n<<1) 28 #define rs (n<<1|1) 29 int sgn(double a) { 30 return a < -eps ? -1 : a < eps ? 0 : 1; 31 } 32 33 //-------------------------- 34 35 const int maxn = 100010; 36 const int mod = 1e9 + 7; 37 38 39 int n, q; 40 int col[maxn]; 41 vector<int> g[maxn]; 42 vector<pii> query[maxn]; 43 unordered_map<int, int > color[maxn]; 44 int ans[maxn]; 45 struct Node { 46 Node *son[2]; 47 int Key, w, Size; 48 Node(int a, int b, int c, Node *d): Key(a), w(b), Size(c) { 49 son[0] = son[1] = d; 50 } 51 }*__root[maxn], *null; 52 void init() { 53 null = new Node(-inf, inf, 0, 0); //这里维护的是小根堆 54 for (int i = 1; i <= n; i++) { 55 __root[i] = null = null->son[0] = null->son[1] = null; 56 57 } 58 } 59 inline void Rotate(Node *&p, bool b) { //Rotate(p,0)表示;左旋Rotate(p,1)表示右旋 60 Node *u = p->son[!b]; 61 p->son[!b] = u->son[b]; 62 u->son[b] = p; 63 u->Size = p->Size; 64 p->Size = p->son[0]->Size + p->son[1]->Size + 1; 65 p = u; 66 } 67 void Insert(Node *&p, const int &x) { //注意引用 68 if (p == null) { //如果找到了位置就插入 69 p = new Node(x, rand(), 1, null); 70 return; 71 } 72 bool b = (x > p->Key); //判断是往左子树还是右子树插入 73 Insert(p->son[b], x); 74 p->Size++; //维护Size 75 if (p->son[b]->w < p->w) Rotate(p, !b); //在左子树就右旋,在右子树就左旋 76 } 77 void Delete(Node *&p, const int &x) { 78 if (p->son[0] == null && p->son[1] == null) { 79 p = null; //这里是等到目标结点变为叶子才删除,仅比常规的删除多做了一次旋转,但代码短多了 80 return; 81 } 82 bool b; //这里的b为if语句后的操作提供了便利 83 if (p->Key == x) { 84 b = (p->son[1]->w > p->son[0]->w); 85 Rotate(p, b); 86 } else b = (x > p->Key); 87 p->Size--; 88 Delete(p->son[b], x); 89 } 90 int Rank(int x, int index) { //返回x的排名(第几小) 91 Node *p = __root[index]; 92 int Count = 0; 93 for (; p != null; ) 94 if (p->Key < x) { 95 Count += p->son[0]->Size + 1; 96 p = p->son[1]; 97 } else p = p->son[0]; 98 return Count + 1; 99 } 100 101 bool Find(int x, int index) { 102 Node *p = __root[index]; 103 for (; p != null;) { 104 if (p->Key < x)p = p->son[1]; 105 else if (p->Key > x)p = p->son[0]; 106 else return true; 107 } 108 return false; 109 } 110 111 void dfs(int u, int fa) { 112 if (Find(color[u][col[u]], u))Delete(__root[u], color[u][col[u]]); 113 color[u][col[u]]++; 114 Insert(__root[u], color[u][col[u]]); 115 for (int i = 0; i < g[u].size(); i++) { 116 int v = g[u][i]; 117 if (v == fa)continue; 118 dfs(v, u); 119 if (color[u].size() < color[v].size()) { 120 swap(color[u], color[v]); 121 swap(__root[u], __root[v]); 122 } 123 for (unordered_map<int, int >::iterator it = color[v].begin(); it != color[v].end(); it++) { 124 if (Find(color[u][it->fi], u))Delete(__root[u], color[u][it->fi]); 125 color[u][it->fi] += it->se; 126 Insert(__root[u], color[u][it->fi]); 127 } 128 } 129 for (int i = 0; i < query[u].size(); i++) { 130 ans[query[u][i].se] = __root[u]->Size - Rank(query[u][i].fi, u) + 1; 131 } 132 133 } 134 135 void solve() { 136 scanf("%d%d", &n, &q); 137 init(); 138 for (int i = 1; i <= n; i++) { 139 scanf("%d", &col[i]); 140 } 141 for (int i = 0; i < n - 1; i++) { 142 int u, v; 143 scanf("%d%d", &u, &v); 144 g[u].push_back(v); 145 g[v].push_back(u); 146 } 147 for (int i = 0; i < q; i++) { 148 int v, k; 149 scanf("%d%d", &v, &k); 150 query[v].push_back(make_pair(k, i)); 151 } 152 dfs(1, -1); 153 for (int i = 0; i < q; i++) { 154 printf("%d\n", ans[i] ); 155 } 156 157 158 } 159 160 161 int main() { 162 163 #ifndef ONLINE_JUDGE 164 freopen("1.in", "r", stdin); 165 // freopen("1.out", "w", stdout); 166 #endif 167 //iostream::sync_with_stdio(false); 168 solve(); 169 return 0; 170 }
分治:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstring> 6 #include<set> 7 #include<vector> 8 #include<queue> 9 #include<map> 10 #include<list> 11 #include<bitset> 12 #include<string> 13 #include<cctype> 14 #include<cstdlib> 15 16 using namespace std; 17 18 typedef long long ll; 19 typedef unsigned long long ull; 20 typedef pair<int, int> pii; 21 #define inf (0x3f3f3f3f) 22 #define lnf (0x3f3f3f3f3f3f3f3f) 23 #define fi first 24 #define se second 25 #define eps (1e-8) 26 int sgn(double a) { 27 return a < -eps ? -1 : a < eps ? 0 : 1; 28 } 29 30 const int maxn = 100010; 31 32 int n, q; 33 int col[maxn]; 34 vector<int> g[maxn]; 35 vector<pii> query[maxn]; 36 int sz[maxn]; 37 int cnt[maxn]; 38 int sum[maxn]; 39 int ans[maxn]; 40 bool big[maxn]; 41 int tot = 0; 42 43 void dfs1(int u, int fa) { 44 sz[u] = 1; 45 for (int i = 0; i < g[u].size(); i++) { 46 int v = g[u][i]; 47 if (v == fa)continue; 48 dfs1(v, u); 49 sz[u] += sz[v]; 50 } 51 } 52 53 void add(int v, int p, int x) { 54 if(x == 1) { 55 sum[++cnt[col[v]]]++; 56 } else { 57 sum[cnt[col[v]]--]--; 58 } 59 for(auto u : g[v]) 60 if(u != p && !big[u]) { 61 add(u, v, x); 62 } 63 } 64 65 void dfs(int v, int p, bool keep) { 66 int mx = -1, bigChild = -1; 67 for(auto u : g[v]) 68 if(u != p && sz[u] > mx) 69 mx = sz[u], bigChild = u; 70 for(auto u : g[v]) 71 if(u != p && u != bigChild) 72 dfs(u, v, 0); 73 if(bigChild != -1) 74 dfs(bigChild, v, 1), big[bigChild] = 1; 75 add(v, p, 1); 76 for (int i = 0; i < query[v].size(); i++) { 77 ans[query[v][i].se] = sum[query[v][i].fi]; 78 } 79 if(bigChild != -1) 80 big[bigChild] = 0; 81 if(keep == 0) 82 add(v, p, -1); 83 } 84 85 void solve() { 86 scanf("%d%d", &n, &q); 87 for (int i = 1; i <= n; i++) { 88 scanf("%d", &col[i]); 89 } 90 for (int i = 0; i < n - 1; i++) { 91 int u, v; 92 scanf("%d%d", &u, &v); 93 g[u].push_back(v); 94 g[v].push_back(u); 95 } 96 for (int i = 0; i < q; i++) { 97 int v, k; 98 scanf("%d%d", &v, &k); 99 query[v].push_back(make_pair(k, i)); 100 } 101 dfs1(1, -1); 102 dfs(1, -1, false); 103 for (int i = 0; i < q; i++) { 104 printf("%d\n", ans[i] ); 105 } 106 107 108 } 109 110 int main() { 111 112 #ifndef ONLINE_JUDGE 113 freopen("1.in", "r", stdin); 114 //freopen("1.out", "w", stdout); 115 #endif 116 //iostream::sync_with_stdio(false); 117 solve(); 118 return 0; 119 }