题目传送门

题意:给你一棵树,树上的每个节点都有树值,给m个查询,问以每个点u为根的子树下有多少种权值恰好出现k次。

分析:首先要对权值离散化,然后要将树形转换为线形,配上图:。然后按照右端点从小到大排序,离线操作:将每一个深度的权值分组到相同权值的cnt中,当sz == k时,用树状数组更新+1,表示在该深度已经存在k个相同的权值,如果>k,之前k个-2(-1是恢复原样,再-1是为下次做准备?),然后一个点的子树的答案就是 sum (r) - sum (l-1)。

当然,区间离线问题用莫队算法是很好做的。

收获:1. 离散化  2. DFS序,树形变线形  3. 离线操作  4. 莫队算法

代码(树状数组):

/************************************************
* Author :Running_Time
* Created Time :2015/9/10 星期四 19:15:22
* File Name :I.cpp
************************************************/ #include <cstdio>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <list>
#include <map>
#include <set>
#include <bitset>
#include <cstdlib>
#include <ctime>
using namespace std; #define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
typedef long long ll;
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
struct Edge {
int v, nex;
}edge[N*2];
struct Query {
int l, r, id;
Query (int _l = 0, int _r = 0, int _id = 0) : l (_l), r (_r), id (_id) {}
bool operator < (const Query &x) const {
return r < x.r;
}
}q[N];
struct BIT {
int c[N];
void init(void) {
memset (c, 0, sizeof (c));
}
void updata(int i, int x) {
while (i < N) {
c[i] += x; i += i & (-i);
}
}
int query(int i) {
int ret = 0;
while (i) {
ret += c[i]; i -= i & (-i);
}
return ret;
}
}bit;
int head[N], dfn[N], low[N], w[N], p[N], val[N], ans[N];
vector<int> cnt[N];
int e, dep; void init(void) {
memset (head, -1, sizeof (head));
e = 0; dep = 0;
} void add_edge(int u, int v) {
edge[e].v = v; edge[e].nex = head[u];
head[u] = e++;
} bool cmp(int i, int j) {
return w[i] < w[j];
} void compress(int n) {
for (int i=1; i<=n; ++i) p[i] = i;
sort (p+1, p+1+n, cmp);
int rk = 0, pre = w[p[1]] - 1;
for (int i=1; i<=n; ++i) {
if (pre != w[p[i]]) {
pre = w[p[i]];
w[p[i]] = ++rk;
}
else {
w[p[i]] = rk;
}
}
} void DFS(int u, int fa) {
dfn[u] = ++dep; val[dep] = w[u];
for (int i=head[u]; ~i; i=edge[i].nex) {
int v = edge[i].v;
if (v == fa) continue;
DFS (v, u);
}
low[u] = dep;
} int main(void) {
int T, cas = 0; scanf ("%d", &T);
while (T--) {
if (cas) puts ("");
printf ("Case #%d:\n", ++cas);
int n, k; scanf ("%d%d", &n, &k);
init (); for (int i=1; i<=n; ++i) {
scanf ("%d", &w[i]);
}
compress (n); //离散化,升序排序,相同的还是相同的 for (int u, v, i=1; i<n; ++i) {
scanf ("%d%d", &u, &v);
add_edge (u, v); add_edge (v, u);
}
DFS (1, -1); //先序遍历,得到DFS序,树形变线形 int m; scanf ("%d", &m);
for (int u, i=1; i<=m; ++i) {
scanf ("%d", &u);
q[i] = Query (dfn[u], low[u], i);
}
sort (q+1, q+1+m); //按照DFS序排序 for (int i=1; i<=n; ++i) cnt[i].clear ();
int qu = 1; bit.init ();
for (int i=1; i<=n; ++i) { //按照dep深度从小到大
int v = val[i];
cnt[v].push_back (i); //表示离散后的相同的权值的个数
int sz = cnt[v].size ();
if (sz >= k) {
if (sz == k) bit.updata (cnt[v][sz-k], 1);
else {
bit.updata (cnt[v][sz-k-1], -2); //?
bit.updata (cnt[v][sz-k], 1);
}
}
while (qu <= m && q[qu].r == i) {
ans[q[qu].id] = bit.query (q[qu].r) - bit.query (q[qu].l - 1);
qu++;
}
} for (int i=1; i<=m; ++i) {
printf ("%d\n", ans[i]);
}
} return 0;
}

代码(莫队算法):

/************************************************
* Author :Running_Time
* Created Time :2015/9/10 星期四 19:15:22
* File Name :I.cpp
************************************************/ #include <cstdio>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <list>
#include <map>
#include <set>
#include <bitset>
#include <cstdlib>
#include <ctime>
using namespace std; #define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
typedef long long ll;
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
struct Edge {
int v, nex;
}edge[N*2];
struct Data {
int l, r, id, b;
Data () {}
Data (int l, int r, int id, int b) : l (l), r (r), id (id), b (b) {}
bool operator < (const Data &x) const {
if (b == x.b) return r < x.r;
else return b < x.b;
}
}data[N];
int head[N], dfn[N], low[N], w[N], p[N], val[N], ans[N];
int cnt[N];
int n, k, m, e, dep, sum; void init(void) {
memset (head, -1, sizeof (head));
e = 0; dep = 0;
} void add_edge(int u, int v) {
edge[e].v = v; edge[e].nex = head[u];
head[u] = e++;
} bool cmp(int i, int j) {
return w[i] < w[j];
} void compress(int n) {
for (int i=1; i<=n; ++i) p[i] = i;
sort (p+1, p+1+n, cmp);
int rk = 0, pre = w[p[1]] - 1;
for (int i=1; i<=n; ++i) {
if (pre != w[p[i]]) {
pre = w[p[i]];
w[p[i]] = ++rk;
}
else {
w[p[i]] = rk;
}
}
} void DFS(int u, int fa) {
dfn[u] = ++dep; val[dep] = w[u];
for (int i=head[u]; ~i; i=edge[i].nex) {
int v = edge[i].v;
if (v == fa) continue;
DFS (v, u);
}
low[u] = dep;
} inline void updata(int x, int c) {
cnt[x] += c;
if (cnt[x] == k) sum++;
else if (c > 0 && cnt[x] == k + 1) sum--;
else if (c < 0 && cnt[x] == k - 1) sum--;
} void Modui(void) {
memset (cnt, 0, sizeof (cnt));
sum = 0;
int l = 1, r = 0;
for (int i=1; i<=m; ++i) {
while (data[i].l < l) updata (val[--l], 1);
while (data[i].l > l) updata (val[l], -1), l++;
while (data[i].r > r) updata (val[++r], 1);
while (data[i].r < r) updata (val[r], -1), r--;
ans[data[i].id] = sum;
}
for (int i=1; i<=m; ++i) {
printf ("%d\n", ans[i]);
}
} int main(void) {
int T, cas = 0; scanf ("%d", &T);
while (T--) {
if (cas) puts ("");
printf ("Case #%d:\n", ++cas);
scanf ("%d%d", &n, &k);
init (); for (int i=1; i<=n; ++i) {
scanf ("%d", &w[i]);
}
compress (n); //离散化,升序排序,相同的还是相同的 for (int u, v, i=1; i<n; ++i) {
scanf ("%d%d", &u, &v);
add_edge (u, v); add_edge (v, u);
}
DFS (1, -1); //先序遍历,得到DFS序,树形变线形 int block = (int) sqrt (n + 0.5);
scanf ("%d", &m);
for (int u, i=1; i<=m; ++i) {
scanf ("%d", &u);
data[i] = Data (dfn[u], low[u], i, dfn[u] / block);
}
sort (data+1, data+1+m); //按照DFS序排序
Modui ();
} return 0;
}

  

Codeforces Round #136 (Div. 1) B. Little Elephant and Array

题目传送门

分析:估计这是上面那道题的母题(弱化版),直接套用就行了,现在稍微理解了树状数组的维护方法,配上图:

代码(树状数组):

/************************************************
* Author :Running_Time
* Created Time :2015/9/11 星期五 18:39:17
* File Name :B.cpp
************************************************/ #include <cstdio>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <list>
#include <map>
#include <set>
#include <bitset>
#include <cstdlib>
#include <ctime>
using namespace std; #define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
typedef long long ll;
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
int n, m;
struct BIT {
int c[N];
void init(void) {
memset (c, 0, sizeof (c));
}
void updata(int i, int x) {
while (i <= n) {
c[i] += x; i += i & (-i);
}
}
int query(int i) {
int ret = 0;
while (i) {
ret += c[i]; i -= i & (-i);
}
return ret;
}
}bit;
struct Query {
int l, r, id;
bool operator < (const Query &x) const {
return r < x.r;
}
}q[N];
int a[N], ans[N];
vector<int> cnt[N]; int main(void) {
scanf ("%d%d", &n, &m);
for (int i=1; i<=n; ++i) scanf ("%d", &a[i]);
for (int i=1; i<=m; ++i) {
scanf ("%d%d", &q[i].l, &q[i].r);
q[i].id = i;
}
sort (q+1, q+1+m);
int qu = 1;
for (int i=1; i<=n; ++i) cnt[i].clear ();
bit.init ();
for (int i=1; i<=n; ++i) {
if (a[i] > n) continue;
cnt[a[i]].push_back (i);
int sz = cnt[a[i]].size ();
if (sz >= a[i]) {
bit.updata (cnt[a[i]][sz-a[i]], 1);
if (sz > a[i]) bit.updata (cnt[a[i]][sz-a[i]-1], -2);
if (sz > a[i] + 1) bit.updata (cnt[a[i]][sz-a[i]-2], 1);
}
while (qu <= m && q[qu].r == i) {
ans[q[qu].id] = bit.query (q[qu].r) - bit.query (q[qu].l - 1);
qu++;
}
}
for (int i=1; i<=m; ++i) {
printf ("%d\n", ans[i]);
} return 0;
}

  

 代码(莫队):

/************************************************
* Author :Running_Time
* Created Time :2015/9/11 星期五 20:05:22
* File Name :B_Modui.cpp
************************************************/ #include <cstdio>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <list>
#include <map>
#include <set>
#include <bitset>
#include <cstdlib>
#include <ctime>
using namespace std; #define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
typedef long long ll;
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
struct Data {
int l, r, id, b;
bool operator < (const Data &x) const {
if (b == x.b) return r < x.r;
else return b < x.b;
}
}data[N];
int a[N], ans[N], cnt[N];
int n, m, sum; inline void updata(int x, int c) {
if (x > n) return ;
cnt[x] += c;
if (cnt[x] == x) sum++;
else if (c > 0 && cnt[x] == x + 1) sum--;
else if (c < 0 && cnt[x] == x - 1) sum--;
} void Modui(void) {
sum = 0;
memset (cnt, 0, sizeof (cnt));
int l = 1, r = 0;
for (int i=1; i<=m; ++i) {
while (data[i].l < l) updata (a[--l], 1);
while (data[i].l > l) updata (a[l], -1), l++;
while (data[i].r > r) updata (a[++r], 1);
while (data[i].r < r) updata (a[r], -1), r--;
ans[data[i].id] = sum;
}
for (int i=1; i<=m; ++i) {
printf ("%d\n", ans[i]);
}
} int main(void) {
scanf ("%d%d", &n, &m);
int block = (int) sqrt (n + 0.5);
for (int i=1; i<=n; ++i) {
scanf ("%d", &a[i]);
}
for (int i=1; i<=m; ++i) {
scanf ("%d%d", &data[i].l, &data[i].r);
data[i].id = i; data[i].b = data[i].l / block;
}
sort (data+1, data+1+m);
Modui (); return 0;
}

  

(好题)树状数组+离散化+DFS序+离线/莫队 HDOJ 4358 Boring counting的更多相关文章

  1. hdu4605 树状数组+离散化+dfs

    Magic Ball Game Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) ...

  2. BZOJ3881[Coci2015]Divljak——AC自动机+树状数组+LCA+dfs序+树链的并

    题目描述 Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: “1 P”,Bob往自己的集合里添加了一个字符串P. ...

  3. BZOJ 3881 [COCI2015]Divljak (Trie图+Fail树+树链的并+树状数组维护dfs序)

    题目大意: Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: “1 P”,Bob往自己的集合里添加了一个字符串P. ...

  4. POJ 2299 Ultra-QuickSort (树状数组 && 离散化&&逆序)

    题意 : 给出一个数n(n<500,000), 再给出n个数的序列 a1.a2.....an每一个ai的范围是 0~999,999,999  要求出当通过相邻两项交换的方法进行升序排序时需要交换 ...

  5. Codeforces 1111E DP + 树状数组 + LCA + dfs序

    题意:给你一颗树,有q次询问,每次询问给你若干个点,这些点可以最多分出m组,每组要满足两个条件:1:每组至少一个点,2:组内的点不能是组内其它点的祖先,问这样的分组能有多少个? 思路:https:// ...

  6. Ultra-QuickSort(树状数组求逆序对数)

    Ultra-QuickSort 题目链接:http://poj.org/problem?id=2299 Time Limit: 7000MS   Memory Limit: 65536K Total ...

  7. BZOJ_5055_膜法师_树状数组+离散化

    BZOJ_5055_膜法师_树状数组+离散化 Description 在经历过1e9次大型战争后的宇宙中现在还剩下n个完美维度, 现在来自多元宇宙的膜法师,想偷取其中的三个维度为伟大的长者续秒, 显然 ...

  8. hdu2838树状数组解逆序

    离散化和排序后的序号问题搞得我实在是头痛 不过树状数组解逆序和偏序一类问题真的好用 更新:hdu的数据弱的真实,我交上去错的代价也对了.. 下面的代码是错的 /* 每个点的贡献度=权值*在这个点之前的 ...

  9. BZOJ3289[JZYZOJP2018]: Mato的文件管理 莫队+树状数组+离散化

            描述 Description     Mato同学从各路神犇以各种方式(你们懂的)收集了许多资料,这些资料一共有n份,每份有一个大小和一个编号.为了防止他人偷拷,这些资料都是加密过的, ...

随机推荐

  1. oracle case where 复杂sql语句

    update hr_user u set u.is_approve=(case when u.curr_org_id in (select t.org_id from hr_organization ...

  2. redis11----监控工具 sentinel

    当master宕机了要把slave1变为master,并把slave2指向slave1. 可以运行时更改master-slave,config set/get,修改一台slave(设为A)为new m ...

  3. Typescript 常见写法

    一.Typescript 中数组 let list: number[] = [1, 2, 3]; let list: Array<number> = [1, 2, 3];

  4. EhCache+Redis实现分布式缓存

    Ehcache集群模式 由于 EhCache 是进程中的缓存系统,一旦将应用部署在集群环境中,每一个节点维护各自的缓存数据,当某个节点对缓存数据进行更新,这些更新的数据无法在其它节点中共享,这不仅会降 ...

  5. 域名ip自动跳转 跳转指定页面的js

    域名ip自动跳转 跳转指定页面的js 为了应对百度审核,需要客户的网站在个别地区跳转到另一个页面,就搞到了这段代码,屡试不爽,超实用.下面把地址换成你要访问的网站url地址或者文件url地址即可.超实 ...

  6. spark运行方式及其常用参数

    yarn cluster模式 例行任务一般会采用这种方式运行 指定固定的executor数 作业常用的参数都在其中指定了,后面的运行脚本会省略 spark-submit \ --master yarn ...

  7. 一些优秀的iOS第三方库

    文章目录 Kits ProgressHUD 加载与刷新 图像 引导页 Views Others Kits RegexKitRegexKit是一个正则表达式工具类. JSONKitJSONKit是一个比 ...

  8. NOIP2007普及 守望者的逃离

    传送门 普及组的题目……很水. 原来写了一个模拟不过好像状态考虑的不全得了80,这次我们考虑一下dp做法. 守卫者有两种移动的方法,一种是闪现,一种是跑,我们可以把闪现和跑分开处理. 首先只处理闪现的 ...

  9. bzoj4516

    后缀自动机 留个板子 upd:大概懂了 每次新加入的npRight集合肯定只有最后一个位置,那么求所有长得不一样的子串贡献就是Max-Min+1,因为Right集合只有这一个位置,所以这Max-Min ...

  10. linux 下 读取某个文件的某一行或者某几行

    wc -l  a.txt  统计a.txt 行数 查看文件a.txt的第190行到196行, sed -n '190,196p' a.txt  如果查看某一行用 sed -n 'a,bp' a.txt ...