HDU5678 dfs序 + 主席树

  附上题目链接:http://bestcoder.hdu.edu.cn/contests/contest_chineseproblem.php?cid=694&pid=1003

  

问题描述
ztr神犇从小就喜欢树,CCTV-少儿“智慧树上智慧果,智慧树下你和我,智慧树前做游戏,欢乐多又多”。
有一天,qzh去找ztr问问题,给一颗有根树,树上的每一个节点有一个权值,每次询问某个子树中所有权值的中位数
ztr:这种水题还用做?qzh表示很无奈,于是qzh找到了神犇的你,你能帮帮他吗?
输入描述
第一行一个正整数T,为数据组数.
每组数据第一行读入两个正整数n,m,分别表示树上的节点数和询问次数.
接下来n个数,第i个数表示编号为i的节点权值val
接下来n-1行,每行两个数u,v表示从u到v有一条边.
接下来m行,每行一个数x表示询问以x为根的子树中所有权值的中位数.
1<=T<=3,1<=n<=10^{5},1<=m<=10^{6},1<=u<=v<=n,1<=val<=10^{9}1<=T<=3,1<=n<=105​​,1<=m<=106​​,1<=u<=v<=n,1<=val<=109​​,1号节点是树的根,保证输入是棵有根树
输出描述
对于每一组数据,输出一行,为了避免输出巨大,你需要把每一个询问后的数hash之后再输出
hash的方法:a[i]表示第i个询问结果 ans = \sum a[i]*10^{m-i}\;mod\;1,000,000,007ans=a[i]10mi​​mod1,000,000,007输出保留一位小数
输入样例
1
5 3
1 2 3 4 5
1 2
2 3
3 4
4 5
1
2
3

分析: 我们可以将一颗树按照dfs序排列, 对于中位数我们可以找出以i为根的第k大实现, 由于中位数中有小数的存在, 因此我们使用pair来存解, 这个技巧挺棒的, 具体看代码。 代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <iostream>
#include <cmath>

using namespace std;
typedef long long LL;
typedef pair<LL, int> pii;
const int maxn = 1000000 + 100;
const LL M =1000000007;
int n, m;   //n个顶点 m个查询
int node_weight[maxn];
int dfsxu[maxn], ndfsxu;
int start[maxn], eend[maxn];
vector<int> G[maxn];

void dfs(int u, int pre, int &k) {
    dfsxu[k] = node_weight[u];
    start[u] = k;    //u顶点的起始点是k
    k++;
    for(int i=0; i<G[u].size(); i++) {
        int v = G[u][i];
        if(v == pre) continue;
        dfs(v, u, k);
    }
    eend[u] = k-1;
}

//主席树
int sorted[maxn];
int toleft[25][maxn];
int tree[25][maxn];

void build_tree(int left, int right, int deep)
{
    int i;
    if (left == right) return ;
    int mid = (left + right) >> 1;
    int same = mid - left + 1; //位于左子树的数据
    for (i = left; i <= right; ++i) {//计算放于左子树中与中位数相等的数字个数
        if (tree[deep][i] < sorted[mid]) {
            --same;
        }
    }
    int ls = left;
    int rs = mid + 1;
    for (i = left; i <= right; ++i) {
        int flag = 0;
        if ((tree[deep][i] < sorted[mid]) || (tree[deep][i] == sorted[mid] && same > 0)) {
            flag = 1;
            tree[deep + 1][ls++] = tree[deep][i];
            if (tree[deep][i] == sorted[mid])
                same--;
        } else {
            tree[deep + 1][rs++] = tree[deep][i];
        }
        toleft[deep][i] = toleft[deep][i - 1]+flag;
    }
    build_tree(left, mid, deep + 1);
    build_tree(mid + 1, right, deep + 1);
}


int query(int left, int right, int k, int L, int R, int deep)
{
    if (left == right)
        return tree[deep][left];
    int mid = (L + R) >> 1;
    int x = toleft[deep][left - 1] - toleft[deep][L - 1];//位于left左边的放于左子树中的数字个数
    int y = toleft[deep][right] - toleft[deep][L - 1];//到right为止位于左子树的个数
    int ry = right - L - y;//到right右边为止位于右子树的数字个数
    int cnt = y - x;//[left,right]区间内放到左子树中的个数
    int rx = left - L - x;//left左边放在右子树中的数字个数
    if (cnt >= k) {
        //printf("sss %d %d %d\n", xx++, x, y);
        return query(L + x, L + y - 1, k, L, mid, deep + 1);
    }
    else {
        //printf("qqq %d %d %d\n", xx++, x, y);
        return query(mid + rx + 1, mid + 1 + ry, k - cnt, mid + 1, R, deep + 1);
    }
}



pii res[maxn];

int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d", &n, &m);
        for(int i=1; i<=n; i++) G[i].clear();
        for(int i=1; i<=n; i++) scanf("%d", &node_weight[i]);
        for(int i=0; i<n-1; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        ndfsxu = 1;
        dfs(1, -1, ndfsxu);
        ndfsxu -= 1;
        for(int i=1; i<=ndfsxu; i++){
            sorted[i] = dfsxu[i];
            tree[0][i] = dfsxu[i];
        }
        sort(sorted+1, sorted+1+ndfsxu);
        build_tree(1, ndfsxu, 0);

        for(int i=1; i<=n; i++) {
            int qq = i;
            int ai;
            if((eend[qq]-start[qq]+1)&1) {
                ai = query(start[qq], eend[qq], (eend[qq]-start[qq]+1)/2+1, 1, n, 0);
                res[i] = (pii){ai, 0};
           //     cout<<"ai = "<<ai<<endl;
            }else{
                int a1 = query(start[qq], eend[qq], (eend[qq]-start[qq]+1)/2, 1, n, 0);
                int a2 = query(start[qq], eend[qq], (eend[qq]-start[qq]+1)/2+1, 1, n, 0);
                res[i] = (pii){(a1+a2)/2, (a1+a2)&1};
            }
        }
//        poww[0] = 1;
//        for(int i=1; i<=1000000; i++)
//            poww[i] = 10*poww[i-1]%M;
        pii ans = (pii){0, 0};
        for(int i=1; i<=m; i++) {
            int x;
            scanf("%d", &x);
            ans.first *= 10;
            if(ans.second) ans.second=0, ans.first+=5;
            ans.first = (ans.first+res[x].first)%M;
            ans.second += res[x].second;
        }
        cout<<ans.first;
        if(ans.second) cout<<".5"<<endl;
        else cout<<".0"<<endl;
    }
    return 0;
}

 

posted @ 2016-05-06 21:44  xing-xing  阅读(239)  评论(0编辑  收藏  举报