题解 P3605 【[USACO17JAN]Promotion Counting晋升者计数】

这道题开10倍左右一直MLE+RE,然后尝试着开了20倍就A了。。。窒息



对于这道题目,我们考虑使用线段树合并来做。

所谓线段树合并,就是把结构相同的线段树上的节点的信息合在一起,合并的方式比较类似左偏树什么的。



我们对于每个节点用权值线段树查询大于它的子节点数量,然后把当前节点并到它的父亲上面去。

对于此类型的题目我们通常使用动态开点的线段树(不然炸的没边)。

时间复杂度应该是O(nlogn)



AC代码如下:

455ms 32824kb

 1 #include <bits/stdc++.h>
 2
 3 using namespace std;
 4
 5 namespace StandardIO {
 6
 7     template<typename T> inline void read (T &x) {
 8         x=0;T f=1;char c=getchar();
 9         for(; c<‘0‘||c>‘9‘; c=getchar()) if(c==‘-‘) f=-1;
10         for(; c>=‘0‘&&c<=‘9‘; c=getchar()) x=x*10+c-‘0‘;
11         x*=f;
12     }
13     template<typename T>inline void write (T x) {
14         if (x<0) putchar(‘-‘),x*=-1;
15         if (x>=10) write(x/10);
16         putchar(x%10+‘0‘);
17     }
18
19 }
20
21 using namespace StandardIO;
22
23 namespace Solve {
24
25     const int N=100100;
26
27     int n;
28     int cnt;
29     struct node {
30         int id,v;
31         inline bool operator < (const node &x) const {
32             return v<x.v;
33         }
34     }p[N];
35     vector<int>graph[N];
36     int tree_node;
37     int val[N],tree[(int)(N*20)],ls[(int)(N*20)],rs[(int)(N*20)],root[N],ans[N];
38
39     void build (int l,int r,int v,int &root) {
40         if (!root) root=++tree_node;
41         tree[root]++;
42         if (l==r) return;
43         int mid=(l+r)>>1;
44         if (v<=mid) build(l,mid,v,ls[root]);
45         else build(mid+1,r,v,rs[root]);
46     }
47     int query (int l,int r,int v,int root) {
48         if (!root) return 0;
49         if (v<=l) return tree[root];
50         int mid=(l+r)>>1;
51         if (v<=mid) return query(l,mid,v,ls[root])+query(mid+1,r,v,rs[root]);
52         return query(mid+1,r,v,rs[root]);
53     }
54     int merge (int x,int y) {
55         if (!x||!y) return x+y;
56         int root=++tree_node;
57         tree[root]=tree[x]+tree[y];
58         ls[root]=merge(ls[x],ls[y]);
59         rs[root]=merge(rs[x],rs[y]);
60         return root;
61     }
62     void dfs (int now) {
63         for (register int i=0; i<graph[now].size(); ++i) {
64             int to=graph[now][i];
65             dfs(to);
66             root[now]=merge(root[now],root[to]);
67         }
68         ans[now]=query(1,cnt,val[now]+1,root[now]);
69         build(1,cnt,val[now],root[now]);
70     }
71
72     inline void solve () {
73         read(n);
74         for (register int i=1; i<=n; ++i) {
75             read(p[i].v),p[i].id=i;
76         }
77         sort(p+1,p+n+1);
78         for (register int i=1; i<=n; ++i) {
79             if (p[i].v!=p[i-1].v) val[p[i].id]=++cnt;
80             else val[p[i].id]=cnt;
81         }
82         for (register int i=2; i<=n; ++i) {
83             int x;read(x);
84             graph[x].push_back(i);
85         }
86         dfs(1);
87         for (register int i=1; i<=n; ++i) {
88             write(ans[i]),putchar(‘\n‘);
89         }
90     }
91 }
92
93 using namespace Solve;
94
95 int main () {
96     solve();
97 }

原文地址:https://www.cnblogs.com/ilverene/p/9841094.html

时间: 2024-10-04 17:20:55

题解 P3605 【[USACO17JAN]Promotion Counting晋升者计数】的相关文章

洛谷P3605 [USACO17JAN]Promotion Counting晋升者计数

洛谷P3605 [USACO17JAN]Promotion Counting晋升者计数 题目描述 奶牛们又一次试图创建一家创业公司,还是没有从过去的经验中吸取教训--牛是可怕的管理者! 为了方便,把奶牛从 $1 \cdots N(1 \leq N \leq 100, 000)$ 编号,把公司组织成一棵树,1 号奶牛作为总裁(这棵树的根节点).除了总裁以外的每头奶牛都有一个单独的上司(它在树上的 “双亲结点”).所有的第 $i$ 头牛都有一个不同的能力指数 $p(i)$,描述了她对其工作的擅长程度

P3605 [USACO17JAN]Promotion Counting晋升者计数 线段树合并 or 树状数组

题意:每个点有一个权值    求每个节点的子树中比其权值大的节点数 线段树合并模板题 #include<bits/stdc++.h> using namespace std; #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define repp(i,a,b) for(int i=(a);i>=(b);--i) #define ll long long #define see(x) (cerr<<(#x)<<'='

[线段树合并] Luogu P3605 [USACO17JAN]Promotion Counting晋升者计数

给一棵 N 个点的树,每个点有一个权值,求每个点的子树中有多少个点的权值比它大. 考虑线段树合并,将权值离散化,每个点开一棵权值线段树. 求答案时直接在权值线段树上查询,线段树合并时类似于可并堆. 要注意的是线段树要动态开点,合并时别忘了 up. 内存什么的最好算一下,数组别开小了. 1 #include<bits/stdc++.h> 2 #define rep(i,a,b) for(register int i=a;i<=b;++i) 3 #define rpd(i,a,b) for(

[USACO17JAN]Promotion Counting晋升者计数

题目描述 奶牛们又一次试图创建一家创业公司,还是没有从过去的经验中吸取教训--牛是可怕的管理者! 为了方便,把奶牛从 1 \cdots N(1 \leq N \leq 100, 000)1?N(1≤N≤100,000) 编号,把公司组织成一棵树,1 号奶牛作为总裁(这棵树的根节点).除了总裁以外的每头奶牛都有一个单独的上司(它在树上的 "双亲结点").所有的第 ii 头牛都有一个不同的能力指数 p(i)p(i),描述了她对其工作的擅长程度.如果奶牛 ii 是奶牛 jj 的祖先节点(例如

[USACO17JAN] Promotion Counting晋升者计数 (树状数组+dfs)

题目大意:给你一棵树,求以某节点为根的子树中,权值大于该节点权值的节点数 本题考查dfs的性质 离散+树状数组求逆序对 先离散 我们发现,求逆序对时,某节点的兄弟节点会干扰答案 所以,我们在递推时统计一次答案,递归时再统计一次答案,两者的差值就是最终结果 #include <bits/stdc++.h> #define dd double #define N 100100 using namespace std; int n,cnt,ma,lst; int a[N],head[N],s[N],

USACO17JAN Promotion Counting

题目传送门 简化题意:一颗带有点权.以\(1\)为根的树,对于每个节点\(x\),求出\(x\)的子树中有多少个点满足该点的点权大于\(x\)的点权 先将点权离散化 对这棵树进行DFS,在DFS到\(x\)时,加入该点点权,然后在DFS它的子树前记录一下当前有多少节点大于\(x\),记为\(last\).在回溯到该节点时再次查询当前有多少节点大于\(x\),减去\(last\)即为答案 #include <iostream> #include <cstdio> #include &

【题解】晋升者计数 Promotion Counting [USACO 17 JAN] [P3605]

[题解]晋升者计数 Promotion Counting [USACO 17 JAN] [P3605] 奶牛们又一次试图创建一家创业公司,还是没有从过去的经验中吸取教训.!牛是可怕的管理者! [题目描述] 奶牛从 \(1\) ~ \(N(1≤N≤1e5)\) 进行了编号,把公司组织成一棵树,\(1\)号奶牛作为总裁(树的根节点).除总裁以外的每头奶牛都有且仅有唯一的一个的上司(即它在树上的父结点).每一头牛\(i\)都有一个不同的能力指数 \(p(i)\),描述了她对其工作的擅长程度.如果奶牛

UVA 10574 - Counting Rectangles(枚举+计数)

10574 - Counting Rectangles 题目链接 题意:给定一些点,求能够成几个矩形 思路:先把点按x排序,再按y排序,然后用O(n^2)的方法找出每条垂直x轴的边,保存这些边两点的y坐标y1, y2.之后把这些边按y1排序,再按y2排序,用O(n)的方法找出有几个连续的y1, y2都相等,那么这些边两两是能构成矩形的,为C2cnt种,然后累加起来就是答案 代码: #include <stdio.h> #include <string.h> #include <

[BZOJ4756][Usaco2017 Jan]Promotion Counting 树状数组

4756: [Usaco2017 Jan]Promotion Counting Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 305  Solved: 217[Submit][Status][Discuss] Description The cows have once again tried to form a startup company, failing to remember from past experience t hat cow