(送给外省的同学们:HD = 海淀)

[HDNOIP201701]小鱼干

试题描述

小喵喵有 n 个小鱼干排成一列,其中第 i 个小鱼干有两种属性,美味度 ai 和特殊度 bi

现在小喵喵要吃掉一些小鱼干,出于一些原因,小喵喵会吃掉连续的一段区间中的所有小鱼干。

如果吃掉了 [l,r] 一段区间,那么小喵喵会获得一些满意度。

形式化地,总满意度 =(∑ai(i∈[l, r]))×(1+∑bi(i∈[l, r]))。

由于只有小喵喵最喜欢的小鱼干的特殊度等于 1,所以 bi=1 的小鱼干数量不会超过 400 个,其他的 bi=0。

现在小喵喵可以选择任意一段区间(可以为空),但是有一些小鱼干的美味度是负数,吃掉所有小鱼干不一定会获得最多的满意度。所以小喵喵想知道最大能获得的总满意度是多少。

输入

第一行一个整数 n,表示小鱼干的数量。

第二行 n 个整数,第 i 个数为 ai,表示美味度。

第三行 n 个整数,第 i 个数为 bi,表示特殊度。

输出

一行一个整数,表示最大的总满意度。

输入示例

  1. - -

输出示例

  1.  

数据规模及约定

对于 60% 的数据,1≤N≤1000。

对于另外 20% 的数据,所有 bi=0。

对于另外 10% 的数据,保证 bi=0 的 i 不会超过 20。

对于 100% 的数据,1≤N≤100000,0≤|ai|≤109,0≤bi≤1,保证 bi=1 的 i 不会超过 400。、

题解

关注到 bi = 1 的 i 的个数不会超过 400,而且我们选择的仅仅是一个区间,所以可以想到枚举最左边和最右边的 1。于是对于每个 bi = 1 的位置 i,预处理一下 mxl[i] 表示从 i-1 向左到上一个 b 的值为 1 的位置这段区间中的最大后缀和;mxr[i] 表示 i+1 向右到下一个 b 的值为 1 的位置这段区间中的最大前缀和。那么假设我们枚举的最左边的 1 的位置是 a,最右边的 1 的位置是 b,用 (mxl[a] + S[b] - S[a-1] + mxr[b])(b - a + 1) 这个值更新答案即可。注意最后再用整个序列的最大连续和更新一下答案。

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstdlib>
  4. #include <cstring>
  5. #include <cctype>
  6. #include <algorithm>
  7. using namespace std;
  8.  
  9. int read() {
  10. int x = 0, f = 1; char c = getchar();
  11. while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
  12. while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
  13. return x * f;
  14. }
  15.  
  16. #define maxn 100010
  17. #define LL long long
  18.  
  19. int n, A[maxn], pos[maxn], cntp;
  20. LL S[maxn], mxl[maxn], mxr[maxn], f[maxn][2];
  21.  
  22. int main() {
  23. n = read();
  24. for(int i = 1; i <= n; i++) S[i] = S[i-1] + (A[i] = read());
  25. for(int i = 1; i <= n; i++) if(read()) pos[++cntp] = i;
  26.  
  27. LL ans = 0;
  28. if(cntp) {
  29. pos[0] = 0; pos[cntp+1] = n + 1;
  30. for(int i = 1; i <= cntp; i++) {
  31. LL tmp = 0;
  32. for(int j = pos[i] - 1; j > pos[i-1]; j--) tmp += A[j], mxl[i] = max(mxl[i], tmp);
  33. tmp = 0;
  34. for(int j = pos[i] + 1; j < pos[i+1]; j++) tmp += A[j], mxr[i] = max(mxr[i], tmp);
  35. }
  36. for(int i = 1; i <= cntp; i++)
  37. for(int j = i; j <= cntp; j++)
  38. ans = max(ans, (mxl[i] + S[pos[j]] - S[pos[i]-1] + mxr[j]) * (j - i + 2));
  39. }
  40. for(int i = 1; i <= n; i++)
  41. f[i][0] = max(f[i-1][0], f[i-1][1]),
  42. f[i][1] = max(f[i-1][1], 0ll) + A[i];
  43. ans = max(ans, max(f[n][0], f[n][1]));
  44.  
  45. printf("%lld\n", ans);
  46.  
  47. return 0;
  48. }

[HDNOIP201702]拓扑排序

试题描述

绵羊送给小喵喵了 n 个小鱼干。小喵喵要把它们全部吃掉!

但是绵羊告诉小喵喵要按照某种顺序来吃。

绵羊一共说了 m 条限制,每条限制的格式为 u v ,表示要先吃掉 u 号小鱼干之后才能吃 v 号小鱼干。

绵羊保证,这些条限制不会出现循环需求的情况,即一定存在某种顺序使得 n 个小鱼干都被吃掉。

绵羊还允许小喵喵不遵守其中小于等于 k 条限制。

小喵喵想知道在吃完所有的小鱼干的前提下,吃小鱼干的顺序的字典序最小是多少。

两个吃小鱼干的顺序的字典序大小比较即为:首先比较第一个吃的小鱼干,编号较小的字典序较小,若相同,则比较第二个吃的小鱼干,一直比到可以分出大小。

注意到两个顺序一定可以比较大小。

输入

第一行为 3 个整数 N、M、K。

第二行至第 M+1 行每行两个正整数 ui、vi,表示 ui 和 vi 有限制关系,即 ui 要比 vi 先吃。

输出

仅一行 N 个正整数,为最优的吃小鱼干的顺序,相邻两个整数之间以空格隔开。

输入示例

  1.  

输出示例

数据规模及约定

对于 30% 的数据,1≤N、M≤8。

对于 60% 的数据,1≤N、M≤20。

对于另外 30% 的数据,K=0。

对于 100% 的数据,1≤N、M≤152501,1≤ui、vi≤N,0≤K≤M。

题解

“字典序最小”,一定先想贪心。

从前往后确定拓扑序上每一位是否可以是当前编号最小的节点,对于节点 u,如果它能放在拓扑序当前为上,须要满足其入度 ≤ K;找到了一个符合条件的 u,则将该节点删除,该节点连出的边也删除。

用一个堆模拟上面的过程就行了,注意我们只用在最初时和每次删除边的时候将节点插入堆中,所以至多插入 n+m 个元素。

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstdlib>
  4. #include <cstring>
  5. #include <cctype>
  6. #include <algorithm>
  7. #include <queue>
  8. using namespace std;
  9.  
  10. int read() {
  11. int x = 0, f = 1; char c = getchar();
  12. while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
  13. while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
  14. return x * f;
  15. }
  16.  
  17. #define maxn 152510
  18. #define oo 2147483647
  19.  
  20. int n, m, K, head[maxn], nxt[maxn], to[maxn], ind[maxn];
  21.  
  22. void AddEdge(int a, int b) {
  23. to[++m] = b; nxt[m] = head[a]; head[a] = m; ind[b]++;
  24. return ;
  25. }
  26.  
  27. priority_queue <int> Q;
  28.  
  29. int main() {
  30. n = read(); int M = read(); K = read();
  31. for(int i = 1; i <= M; i++) {
  32. int a = read(), b = read();
  33. AddEdge(a, b);
  34. }
  35.  
  36. for(int i = 1; i <= n; i++) if(ind[i] <= K) Q.push(-i);
  37. for(int i = 1; i <= n; i++) {
  38. int u = -Q.top(); Q.pop();
  39. while(ind[u] > K) u = -Q.top(), Q.pop();
  40. printf("%d%c", u, i < n ? ' ' : '\n');
  41. K -= ind[u]; ind[u] = oo;
  42. for(int e = head[u]; e; e = nxt[e]) if(ind[to[e]] < oo && --ind[to[e]] <= K) Q.push(-to[e]);
  43. }
  44.  
  45. return 0;
  46. }

[HDNOIP201703]收集珠宝

试题描述

喵国有 n 个村镇,由 n−1 条无向道路连通。来自喵哈哈村的小喵喵想要周游各地。

旅途的路非常漫长,小喵喵决定在路上买一些宝石收藏,其中第 i 个村镇的宝石价格为 Ai

小喵喵的策略是这样的:在一个起点村落购买宝石,之后路上如果当前的村落的宝石价格比小喵喵手上最贵的宝石贵,那么小喵喵就会购买当前村落的宝石。

现在小喵喵有 q 次独立的旅行(即进行新的一次旅行时手上的宝石都会消失),小喵喵想知道如果从 u 村落沿最短路旅行到 v 村落会购买多少次宝石。

输入

第一行为 1 个正整数 N。

第二行为 N 个正整数 Ai

第三行至第 N+1 行每行两个正整数 xi、yi,表示 xi 和 yi 之间有一条道路。

第 N+2 行为一个正整数 Q。

接下来 Q 行每行一个正整数 ui、vi,表示一次询问。

输出

Q 行每行一个正整数,表示答案。

输入示例

  1.  

输出示例

  1.  

数据规模及约定

对于 20% 的数据,1≤N、Q≤2501。

对于另外 15% 的数据,所有ui=1。

对于另外 15% 的数据,所有vi=1。

对于另外 40% 的数据,1≤N、Q≤152501,所有yi=xi+1。

对于 100% 的数据,1≤N、Q≤252501,1≤Ai≤109,1≤ui、vi、xi、yi≤N。

题解

首先将询问拆成两部分,一段上行,一段下行;对于询问 (u, v)(即从 u 到 v,令 c = u 和 v 的最近公共祖先),拆成 (u, c)(上行)和 (c, v)(下行)。

我们发现就是找这样一条链上单调栈的大小。如果把它变成一个序列问题,即查询区间内单调栈的大小,这个问题就可以用线段树解决。

具体是这样的:我们令 query(node, val) 表示对于线段树节点 node 所对应的区间第一个元素 ≥ val 的单调栈的大小,分两种情况考虑:

1. node->l.maxv <= val,即左边的所有值都小于等于 val,直接跳过左半边,返回 query(node->r, val)

2. node->l.maxv > val,返回 node.size - (node->l.size - query(node->l, val) ),其中 node.size 表示节点 node 所对应区间的单调栈的大小,node->l.size - query(node->l, val) 表示左节点单调栈中小于 val 的部分,所以要减去

maxv 不用说了,size 如何维护?我们只需要写一个 update() 函数即可。update() 函数中,设当前节点为 node

1. 如果当前是叶节点,直接将 node.size 设为 1

2. 否则 node.size = node->l.size + query(node->r, node->l.maxv),即左边的单调栈肯定是直接拼上来,右边就是从左边最大值开始的单调栈

这个线段树还需要支持区间查询,实现方法类似,留个读者思考。

下面我们需要把一条链转换成一个序列,好在拆解询问后,每条链都是“儿子 - 祖先”的,所以可以 dfs,然后动态地在线段树中进行修改即可。

注意,求 lca 要用树链剖分,因为要保证空间线性!(注:此题空间限制 64MB)

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstdlib>
  4. #include <cstring>
  5. #include <cctype>
  6. #include <algorithm>
  7. using namespace std;
  8.  
  9. int read() {
  10. int x = 0, f = 1; char c = getchar();
  11. while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
  12. while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
  13. return x * f;
  14. }
  15.  
  16. #define maxn 252510
  17. #define maxm 505020
  18. #define maxlog 18
  19.  
  20. int n, A[maxn], m, head[maxn], nxt[maxm], to[maxm];
  21.  
  22. void AddEdge(int a, int b) {
  23. to[++m] = b; nxt[m] = head[a]; head[a] = m;
  24. swap(a, b);
  25. to[++m] = b; nxt[m] = head[a]; head[a] = m;
  26. return ;
  27. }
  28.  
  29. int dep[maxn], siz[maxn], fa[maxn], son[maxn], top[maxn];
  30. void build(int u) {
  31. siz[u] = 1;
  32. for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa[u]) {
  33. dep[to[e]] = dep[u] + 1;
  34. fa[to[e]] = u;
  35. build(to[e]);
  36. siz[u] += siz[to[e]];
  37. if(!son[u] || siz[to[e]] > siz[son[u]]) son[u] = to[e];
  38. }
  39. return ;
  40. }
  41. void gett(int u, int tp) {
  42. top[u] = tp;
  43. if(son[u]) gett(son[u], tp);
  44. for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa[u] && to[e] != son[u]) gett(to[e], to[e]);
  45. return ;
  46. }
  47. int lca(int a, int b) {
  48. while(top[a] != top[b]) {
  49. if(dep[top[a]] < dep[top[b]]) swap(a, b);
  50. a = fa[top[a]];
  51. }
  52. return dep[a] < dep[b] ? a : b;
  53. }
  54.  
  55. struct Que {
  56. int a, b, c, trn, res;
  57. Que() {}
  58. Que(int _, int __): a(_), b(__), c(lca(_, __)) {}
  59. } qs[maxn];
  60. int q;
  61.  
  62. int mxv[maxn<<2], ssiz[maxn<<2];
  63. int query(int o, int l, int r, int v) {
  64. if(l == r) return mxv[o] > v;
  65. int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
  66. if(v >= mxv[lc]) return query(rc, mid + 1, r, v);
  67. return ssiz[o] - ssiz[lc] + query(lc, l, mid, v);
  68. }
  69. void update(int o, int l, int r, int p, int v) {
  70. if(l == r) mxv[o] = v, ssiz[o] = 1;
  71. else {
  72. int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
  73. if(p <= mid) update(lc, l, mid, p, v);
  74. else update(rc, mid + 1, r, p, v);
  75. mxv[o] = max(mxv[lc], mxv[rc]);
  76. ssiz[o] = ssiz[lc] + query(rc, mid + 1, r, mxv[lc]);
  77. }
  78. return ;
  79. }
  80. struct Snode { // Segment_tree node
  81. int o, l, r;
  82. Snode() {}
  83. Snode(int _1, int _2, int _3): o(_1), l(_2), r(_3) {}
  84. } qnode[maxlog<<2];
  85. int cntq;
  86. void getqnode(int o, int l, int r, int ql, int qr) {
  87. if(ql <= l && r <= qr) qnode[++cntq] = Snode(o, l, r);
  88. else {
  89. int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
  90. if(ql <= mid) getqnode(lc, l, mid, ql, qr);
  91. if(qr > mid) getqnode(rc, mid + 1, r, ql, qr);
  92. }
  93. return ;
  94. }
  95. int _v;
  96. int ask(int ql, int qr, int v) {
  97. cntq = 0; getqnode(1, 1, n, ql, qr);
  98. int res = 0;
  99. for(int i = 1; i <= cntq; i++)
  100. res += query(qnode[i].o, qnode[i].l, qnode[i].r, v),
  101. v = max(v, mxv[qnode[i].o]);
  102. _v = v;
  103. return res;
  104. }
  105.  
  106. struct QOT { // Queries On Tree
  107. int head[maxn], nxt[maxn];
  108. void clear() {
  109. memset(head, 0, sizeof(head));
  110. return ;
  111. }
  112. void AddQ(int u, int qid) {
  113. nxt[qid] = head[u]; head[u] = qid;
  114. return ;
  115. }
  116. } qt;
  117. int segpos[maxn];
  118. void dfs1(int u, int pos) {
  119. segpos[u] = pos;
  120. update(1, 1, n, pos, A[u]);
  121. for(int e = qt.head[u]; e; e = qt.nxt[e])
  122. qs[e].res = ask(pos, segpos[qs[e].c], A[qs[e].a] - 1),
  123. qs[e].trn = _v;
  124. for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa[u]) dfs1(to[e], pos - 1);
  125. return ;
  126. }
  127. void dfs2(int u, int pos) {
  128. segpos[u] = pos;
  129. update(1, 1, n, pos, A[u]);
  130. for(int e = qt.head[u]; e; e = qt.nxt[e])
  131. qs[e].res += ask(segpos[qs[e].c], pos, qs[e].trn);
  132. for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa[u]) dfs2(to[e], pos + 1);
  133. return ;
  134. }
  135.  
  136. int main() {
  137. n = read();
  138. for(int i = 1; i <= n; i++) A[i] = read();
  139. for(int i = 1; i < n; i++) {
  140. int a = read(), b = read();
  141. AddEdge(a, b);
  142. }
  143.  
  144. build(1);
  145. gett(1, 1);
  146. q = read();
  147. for(int i = 1; i <= q; i++) {
  148. int u = read(), v = read();
  149. qs[i] = Que(u, v);
  150. qt.AddQ(u, i);
  151. }
  152. dfs1(1, n);
  153. memset(mxv, 0, sizeof(mxv));
  154. memset(ssiz, 0, sizeof(ssiz));
  155. qt.clear();
  156. for(int i = 1; i <= q; i++) qt.AddQ(qs[i].b, i);
  157. dfs2(1, 1);
  158.  
  159. for(int i = 1; i <= q; i++) printf("%d\n", qs[i].res);
  160.  
  161. return 0;
  162. }

[HDNOIP2017提高组]题解的更多相关文章

  1. noip2010提高组题解

    NOIP2010提高组题解 T1:机器翻译 题目大意:顺序输入n个数,有一个队列容量为m,遇到未出现元素入队,求入队次数. AC做法:直接开1000的队列模拟过程. T2:乌龟棋 题目大意:有长度为n ...

  2. NOIP 2014 提高组 题解

    NOIP 2014 提高组 题解 No 1. 生活大爆炸版石头剪刀布 http://www.luogu.org/problem/show?pid=1328 这是道大水题,我都在想怎么会有人错了,没算法 ...

  3. NOIP 2001 提高组 题解

    NOIP 2001 提高组 题解 No 1. 一元三次方程求解 https://vijos.org/p/1116 看见有人认真推导了求解公式,然后猥琐暴力过的同学们在一边偷笑~~~ 数据小 暴力枚举即 ...

  4. NOIP 2000 提高组 题解

    NOIP2000 提高组 题解 No 1. 进制转换 https://www.rqnoj.cn/problem/295 水题 对于n和基数r, 每次用n mod r, 把余数按照逆序排列 注意 mod ...

  5. 【NOIP2018】提高组题解

    [NOIP2018]提高组题解 其实就是把写过的打个包而已 道路铺设 货币系统 赛道修建 旅行 咕咕咕 咕咕咕

  6. noip2009提高组题解

    NOIP2009题解 T1:潜伏者 题目大意:给出一段密文和破译后的明文,一个字母对应一个密文字母,要求破译一段密文,如果有矛盾或有未出现密文无法破译输出failed,否则输出明文. 思路:纯模拟题 ...

  7. noip2008提高组题解

    第一题:笨小猴 模拟   第二题:火柴棒等式 搜索 深搜不用说,确定出两个加数然后判断能否拼出等式. 枚举确实不太好搞,因为枚举范围不确定,太大了容易超时,太小了容易漏解.不过这题的数据貌似很温和,我 ...

  8. noip2007提高组题解

    题外话:这一年的noip应该是最受大众关心的,以至于在百度上输入noip第三个关键字就是noip2007.主要是由于这篇文章:http://www.zhihu.com/question/2110727 ...

  9. noip2002提高组题解

    再次280滚粗.今天早上有点事情,所以做题的时候一直心不在焉,应该是三天以来状态最差的一次,所以这个分数也还算满意了.状态真的太重要了. 第一题:均分纸牌 贪心.(昨天看BYVoid的noip2001 ...

随机推荐

  1. RenderBody,RenderPage和RenderSection

    1. RenderBody 在Razor引擎中没有了“母版页”,取而代之的是叫做“布局”的页面(_Layout.cshtml)放在了共享视图文件夹中.在这个页面中,会看到<body>标签里 ...

  2. Maven:项目结构

    目录结构图: project        |- src            |- main   //工程源代码目录                |- java        //工程java源代 ...

  3. jq封装插件,简单dome

    (function($) { $.fn.extend({ bold: function() { this.css({ fontWeight: "bold", color: 'red ...

  4. 洛谷 P1516 青蛙的约会

    https://www.luogu.org/problemnew/show/P1516#sub 题意还是非常好理解的..... 假如这不是一道环形的跑道而是一条直线,你会怎样做呢? 如果是我就会列一个 ...

  5. shell 练习题 - 第三周

    1.编写脚本/root/bin/backup.sh,可实现每日将/etc/目录备份到 /root/etcYYYY-mm-dd中 #!/bin/bash echo "start backup& ...

  6. v2ex站长专访 - 100offer专访Livid:不仅仅是V站站长

    转载自: https://www.douban.com/group/topic/121611313/ 前几天上网时偶然发现v2ex站长的blog(https://livid.v2ex.com/),了解 ...

  7. settings.py常规配置项

    settings.py常见配置项 1. 配置Django_Admin依照中文界面显示 LANGUAGE_CODE = 'zh-hans' 2. 数据库配置(默认使用sqlite3) 使用MySQL的配 ...

  8. python里字典的用法介绍

    一.什么是字典 字典是python里的一种数据类型,特点是元素的无序性,和键key的唯一性.字典的创建方法是{key:values},字典里的键key只能是不可变的数据类型(整型,字符串或者是元组), ...

  9. OverflowError:django signed integer is greater than maximum

    在学习一对一查询的时候,打印作者的电话时报了这个错 alex = Author.objects.filter(name='alex').first() print(alex.authordetail. ...

  10. STM32三种启动模式 boot0 boot1

    STM32三种启动模式对应的存储介质均是芯片内置的,它们是: 1)用户闪存=芯片内置的Flash.2)SRAM=芯片内置的RAM区,就是内存啦.3)系统存储器=芯片内部一块特定的区域,芯片出厂时在这个 ...