「NOIP 2017」逛公园

乱七八糟

我好弱啊

这一题只想到递推 DP

又没有想出正解

到现在还是这么菜

NOIP 2019 如何 500+

首先发现 kk 很小,自然地想到 DP

dd 表示最短路

fu,xf_{u, x} 代表从 ssuu 路径长为 du+xd_u + x 的方案数量

阅读更多

「SDOI 2009」HH的项链

称贝壳的种类为颜色

考虑离线做法,将询问按照 rr 升序排序

发现只需要维护每个颜色的目前最右出现位置( r\le r

每个颜色只在最后出现的地方进行统计,显然可以保证最优(因为离线)

举个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
颜色序列为
1 3 1 2 1

扫到 1 (位置)
1 0 0 0 0
扫到 2
1 1 0 0 0
扫到 3
0 1 1 0 0
扫到 4
0 1 1 1 0
扫到 5
0 1 0 1 1

0/1 序列用树状数组维护即可,答案即是 0/1 序列 sum(l,r)sum(l, r)

在线做法,用可持久化线段树即可

阅读更多

「POI 2015」WIL-Wilcze doły

假设选中区间 [l,r][l, r],经过题目中的操作,获得的和为该区间的得分,其中最小的成为最小得分 fl,rf_{l,r}

显然得选满 dd 长度

所以对于区间 [l,r][l, r] 只需要知道其中长度为 dd 的最大子段和就可以知道区间的最小得分

当然是用线段树维护啦

学数据结构果然会变傻

发现可以用单调队列维护最大子段和

然后发现若是区间 fl,r>pf_{l, r} > p 则必然有  x<l,y>r   fx,y>p\forall~x<l,y>r~~~f_{x, y} > p

然后就是一道 two-pointer 的题了

阅读更多

小白逛公园

带修改最大子段和查询

考虑维护一棵线段树,节点 [l,r][l, r] 记录

  • 最大子段和 mm
  • ll 开始的最大子段和 elel
  • rr 结尾的最大子段和 erer

可以非常方便地合并

然后考虑查询区间 [L,R][L, R],当前节点为 [l,r][l, r] ,保证 [l,r][L,R][l,r]\cap[L,R]\neq \varnothing

  1. [l,r][L,R][l, r]\subseteq [L, R] 返回 (el, er) ,更新 ans
  2. rmidr \le mid 返回 q(ls)
  3. l>midl > mid 返回 q(rs)
  4. lmid<rl \le mid < r 返回合并结果,更新 ans

一开始觉得 2 3 42\ 3\ 4 这么做不太严谨,但其实是可以保证正确性的

因为若 elelrlrl 不在 [L,R][L, R] 时,它们也不会再被用作更新 ans,反证法可证

阅读更多

花神游历各国

因为 ai1012a_i\le 10^{12} 所以发现开非常少次数的根号就会到达 11,使答案不再改变

于是考虑对 [L,R][L,R] 区间开根时,若 maxai1\max a_i \le 1 就不用操作了

通过在递归过程中判断实现

所以线段树维护区间和与区间最大

辣鸡 BZOJ 评测机,笔记本都只要跑 300ms,在你的老爷 CPU 上直接 TLE

阅读更多

有线电视网

这是一道树形 DP

首先是状态设计,由于用户人数相比耗费作为数组维度比较简单,所以这样设计

ff[节点编号][用户数] = 最大利润( 可以 <0<0

考虑节点 uu,如何从子节点 vv 得到

把子节点的状态看成一种物品,就是分组背包了

外层倒序枚举 uu 的用户(状态),内层枚举 vv 人数(看成背包中的物品)

1
2
3
4
5
per(i, s, 1)
rep(j, 1, t){
if (i - j < 0) break;
f[u][i] = max(f[u][i], f[u][i-j] + f[v][j] + w)
}

复杂度不知道怎么证

阅读更多

「Codeforces 359B」Permutation

观察

i=1na2i1a2ii=1na2i1a2i=2k \sum_{i=1}^n|a_{2i-1}-a_{2i}|-|\sum_{i=1}^na_{2i-1}-a_{2i}|=2k

发现一个绝对值在外,一个绝对值在里

a0,a+c0a+c=a+c+2a a\le 0, a+c\ge0\\ |a|+|c|=|a+c|+2|a|
a,c0a+c=a+c a,c\ge 0\\ |a|+|c|=|a+c|

类似的

a1a2+a3a4+=2k+a1a2+a3a4 |a_1-a_2|+|a_3-a_4|+\cdots=2k+|a_1-a_2+a_3-a_4\cdots|

只要令

a1a2=k i[2,n], a2i1a2i>0 a_1-a_2=-k\\ \forall\ i\in[2,n],\ a_{2i-1}-a_{2i}> 0

就可满足

例如 n=6,k=2n=6,k=2

1 3 12 11 10 9 8 7 6 5 4 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#define rep(i, l, r) for (int i = (l); i <= (r); ++i)
#define per(i, l, r) for (int i = (l); i >= (r); --i)
using std::cin;
using std::cout;
int main() {
#ifdef LOCAL
freopen("input", "r", stdin);
#endif
std::ios::sync_with_stdio(false);
cout.tie(0);
int n, k;
cin >> n >> k;
if (k == 0) {
per(i, 2 * n, 1) cout << i << ' ';
return 0;
}
cout << 1 << ' ' << k + 1 << ' ';
per(i, 2 * n, 2) {
if (i == k + 1) continue;
cout << i << ' ';
}
return 0;
}

Jury Compromise

尽量充分利用状态,降低复杂度,另外一道题

状态设计比较巧妙

FF[ 剩余空间 ][ 辩方总分 - 控方总分 ] = 辩方总分 + 控方总分

跑一个背包

F[v+1,x+aibi]=max{F[v,x]+ai+bi,F[v+1,x+aibi]}F[v+1, x + a_i - b_i] = \max\{F[v, x] + a_i + b_i, F[v+1, x + a_i - b_i]\}

找方案比较麻烦,如果直接记由哪个状态转移来是不行的,因为后续更新会破坏

所以要搞一个类似链表的东西

阅读更多

「POJ 1201」Intervals

这是一个贪心+线段树的解法,复杂度是 O(nlog2n)O(n\log^2 n)

把要求按 bb 升序排序,修改时用二分套线段树

其他题解怎么都是一个一个跳的,复杂度不会挂吗?

线段树部分写得比较神奇

阅读更多