• 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
    ", xx++, x, y);
            return query(L + x, L + y - 1, k, L, mid, deep + 1);
        }
        else {
            //printf("qqq %d %d %d
    ", 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;
    }
  • 相关阅读:
    c++链表实现学生成绩管理系统(简易版)
    IOS动画讲解
    栈的实现
    Masonry的使用
    二叉树详解-2
    二叉树详解-1
    CoreData的使用-2
    NSPredicate 详解
    CoreData的使用-1
    IOS常用手势用法
  • 原文地址:https://www.cnblogs.com/xingxing1024/p/5467201.html
Copyright © 2020-2023  润新知