POJ 2104, SPOJ MKTHNUM - K-th Number - 静态区间第K大,可持久化线段树

推荐这位大牛的博文,讲得很清晰易懂:http://www.cnblogs.com/zinthos/p/3899565.html

节点个数很多时似乎用new动态开内存的方法会显著变慢,节点个数较少时倒没啥感觉。

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 
  5 const int maxN = (int)1e5 + 10;
  6 
  7 int A[maxN], AProj[maxN], mapped[maxN];
  8 int N, K;
  9 
 10 void input()
 11 {
 12     scanf("%d%d", &N, &K);
 13     for (int i = 1; i <= N; i++)
 14         scanf("%d", A + i);
 15 
 16     memcpy(mapped + 1, A + 1, sizeof(int) * N);
 17     std::sort(mapped + 1, mapped + N + 1);
 18 
 19     for (int i = 1; i <= N; i++)
 20         AProj[i] = int(std::lower_bound(mapped + 1, mapped + N + 1, A[i]) - mapped);
 21 }
 22 
 23 struct Node
 24 {
 25     int cnt;
 26     int ch[2];
 27 };
 28 Node node[maxN * 20];
 29 int root[maxN];
 30 int cnt = 0;
 31 
 32 inline int newNode() { return cnt++; }
 33 
 34 int _init0(int l, int r)
 35 {
 36     if (l + 1 == r)
 37         return newNode();
 38 
 39     int res = newNode();
 40     int m = (l + r) >> 1;
 41     node[res].ch[0] = _init0(l, m);
 42     node[res].ch[1] = _init0(m, r);
 43 
 44     return res;
 45 }
 46 
 47 int insert(int val, int prev)
 48 {
 49     int l = 1, r = N + 1;
 50     int res = newNode();
 51     int cur = res;
 52 
 53     while (true)
 54     {
 55         node[cur] = node[prev];
 56         node[cur].cnt += 1;
 57 
 58         if (l + 1 == r)
 59             break;
 60 
 61         int m = (l + r) >> 1;
 62         int dir = (val < m ? 0 : 1);
 63         val < m ? r = m : l = m;
 64 
 65         cur = node[cur].ch[dir] = newNode();
 66         prev = node[prev].ch[dir];
 67     }
 68 
 69     return res;
 70 }
 71 
 72 void build()
 73 {
 74     root[0] = _init0(1, N + 1);
 75     for (int i = 1; i <= N; i++)
 76         root[i] = insert(AProj[i], root[i - 1]);
 77 }
 78 
 79 int query(int l, int r, int k) //[l, r]
 80 {
 81     int pl = root[l - 1], pr = root[r];
 82     int nl = 1, nr = N + 1;
 83 
 84     while (nl + 1 < nr)
 85     {
 86         int ls = node[node[pr].ch[0]].cnt - node[node[pl].ch[0]].cnt;
 87         int mid = (nl + nr) >> 1;
 88         int dir = (ls < k ? 1 : 0);
 89 
 90         if (ls < k)
 91             k -= ls;
 92         dir == 0 ? nr = mid : nl = mid;
 93         pl = node[pl].ch[dir];
 94         pr = node[pr].ch[dir];
 95     }
 96 
 97     return nl;
 98 }
 99 
100 int main()
101 {
102     input();
103     build();
104 
105     for (int l, r, k, i = 1; i <= K; i++)
106     {
107         scanf("%d%d%d", &l, &r, &k);
108         printf("%d\n", mapped[query(l, r, k)]);
109     }
110     return 0;
111 }

 

实现代码:

 

转载于:https://www.cnblogs.com/Onlynagesha/p/8796505.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
POJ - 3616是一个题目,题目描述如下: 给定一组区间,每个区间有一个权重,要求选择一些区间,使得这些区间的右端点都小于等于k,并且权重之和最大。请问最大的权重和是多少? 解决这个问题的思路是使用动态规划。首先,将区间按照左端点从小到大进行排序。然后,定义一个dp数组,dp[i]表示右端点小于等于i的所有区间所能得到的最大权重。 接下来,遍历每一个区间,对于每个区间i,将dp[i]初始化为区间i的权重。然后,再遍历i之前的每个区间j,如果区间j的右端点小于等于k,并且区间j的权重加上区间i的权重大于dp[i],则更新dp[i]为dp[j]加上区间i的权重。 最后,遍历整个dp数组,找到最大的权重和,即为所求的答案。 下面是具体的代码实现: ```cpp #include <cstdio> #include <cstring> #include <algorithm> using namespace std; struct interval{ int start, end, weight; }; interval intervals[10005]; int dp[10005]; int n, m, k; bool compare(interval a, interval b) { if (a.start == b.start) { return a.end < b.end; } else { return a.start < b.start; } } int main() { while(~scanf("%d %d %d", &n, &m, &k)) { memset(dp, 0, sizeof dp); for (int i = 0; i < m; i++) { scanf("%d %d %d", &intervals[i].start, &intervals[i].end, &intervals[i].weight); } sort(intervals, intervals + m, compare); for (int i = 0; i < m; i++) { dp[i] = intervals[i].weight; for (int j = 0; j < i; j++) { if (intervals[j].end <= k && dp[j] + intervals[i].weight > dp[i]) { dp[i] = dp[j] + intervals[i].weight; } } } int maxWeight = 0; for (int i = 0; i < m; i++) { maxWeight = max(maxWeight, dp[i]); } printf("%d\n", maxWeight); } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值