树状数组+离散化+DFS序+离线 HDOJ 4358 Boring counting

题目传送门

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

分析:首先要对权值离散化,然后要将树形转换为线形,配上图:

收获:

//还没写完。。。

代码:

/************************************************
* 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;
}

  

时间: 2024-10-05 04:44:52

树状数组+离散化+DFS序+离线 HDOJ 4358 Boring counting的相关文章

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

题意 : 给出一个数n(n<500,000), 再给出n个数的序列 a1.a2.....an每一个ai的范围是 0~999,999,999  要求出当通过相邻两项交换的方法进行升序排序时需要交换的次数 分析:其实经过一次模拟后,会发现奇妙的东西,这个排序都是按位置排的,最大要求到最大,最小要去到最小,转化思想这是一道求逆序对数的题目,答案就是逆序对数. 这里数据过大999999999,数组无法开的了这么大,我们可以离散化,只记录相对大小. 这里离散化有所不同,这里为了压时,用了空间换时间的方法.

BZOJ 1227 [SDOI2009] 虔诚的墓主人 离线+树状数组+离散化

鸣谢:140142耐心讲解缕清了我的思路 题意:由于调这道题调的头昏脑涨,所以题意自己搜吧,懒得说. 方法:离线+树状数组+离散化 解析:首先深表本蒟蒻对出题人的敬(bi)意(shi).这道题简直丧心病狂,看完题后大脑一片空白,整个人都不好了,刚开始的思路是什么呢?暴力思想枚举每个墓碑,然后计算每个墓碑的虔诚度,然后再来统计.不过看看数据范围呢?10^9*10^9的矩阵,最多才10^5个树,光枚举就已经超时了,所以肯定不行.(不过要是考试真没思路我就那么搞了- -!) 然后也想到来枚举墓碑,不过

poj2299(离散化+树状数组求逆序)

数据范围比较大,先用离散化将数据映射到可控的范围,然后应用树状数组求逆序求解. 总共有N个数,如何判断第i+1个数到最后一个数之间有多少个数小于第i个数呢?不妨假设有一个区间 [1,N],只需要判断区间[i+1,N]之间有多少个数小于第i个数.如果我们把总区间初始化为0,然后把第i个数之前出现过的数都在相应的区间把它的值定为1,那么问题就转换成了[i+1,N]值的总和.再仔细想一下,区间[1,i]的值+区间[i+1,N]的值=区间[1,N]的值(i已经标记为1),所以区间[i+1,N]值的总和等

求逆序数模板(树状数组+离散化 || 归并排序法)

一篇不错的讲解:http://www.cnblogs.com/shenshuyang/archive/2012/07/14/2591859.html 代码如下:(树状数组+离散化) #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn=500017; int n; int aa[maxn

Ultra-QuickSort (树状数组离散化)

题目原意是用归并排序,刚学树状数组,就用了下 树状数组的离散化 离散化,是数据范围太大是所借用的利器,举个例子,有四个数99999999 1 123 1583 数据范围太大,而树状数组中的c数组开的范围是数据的范围,这时候就需要离散化,把四个数一次标号为1 2 3 4(即第一个数,第二个数...),按键值排序之后 依次为2 3 4 1(即从小到大排序为第二个数,第三个数...),所以,第二个数是最小的,即f[2]=1,f[3]=2,f[4]=3,f[1]=4,也就是把键值变为了1~n,相对大小还

HDU 2227 Find the nondecreasing subsequences (DP+树状数组+离散化)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2227 Find the nondecreasing subsequences                                  Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)                                             

hdu4325 树状数组+离散化

http://acm.hdu.edu.cn/showproblem.php?pid=4325 Problem Description As is known to all, the blooming time and duration varies between different kinds of flowers. Now there is a garden planted full of flowers. The gardener wants to know how many flower

高桥低桥(树状数组离散化)

1335: 高桥和低桥 Time Limit: 1 Sec  Memory Limit: 128 MB Submit: 362  Solved: 62 [Submit][Status][Web Board] Description 有个脑筋急转弯是这样的:有距离很近的一高一低两座桥,两次洪水之后高桥被淹了两次,低桥却只被淹了一次,为什么?答案是:因为低桥太低了,第一次洪水退去之后水位依然在低桥之上,所以不算"淹了两次".举例说明: 假定高桥和低桥的高度分别是5和2,初始水位为1 第一次

Ultra-QuickSort(树状数组 + 离散化)

Description In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swapping two adjacent sequence elements until the sequence is sorted in ascending order. For the input seque