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]∗10m−imod1,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; }