(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 }
View Code

 

启发式合并:

  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 }
View Code

 

分治:

  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 }
View Code

 

posted @ 2017-06-13 13:29  tak_fate  阅读(271)  评论(0编辑  收藏  举报