这是本文档旧的修订版!
| = | Penalty | * | A | B | C | D | E | F | G | H | I | J | K | L | M |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 16 | 2726 | + 00:10 | +2 00:44 | +1 02:19 | +4 04:46 | +3 03:13 | + 03:47 | +1 00:27 | + 00:58 | ||||||
| N | O | P | Q | R | S | T | U | V | W | X | Y | Z | |||
| +1 01:26 | +6 02:33 | +5 01:53 | + 00:58 | + 03:53 | +3 03:39 | + 04:58 | +1 00:42 |
| Solved | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Pantw | √ | √ | √ | √ | √ | √ | ||||||||||||||||||||
| Withinlover | √ | √ | √ | √ | √ | |||||||||||||||||||||
| Gary | √ | √ | √ | √ | √ |
(√ for solved, O for upsolved, - for tried but not solved)
n个人,每个人填一行需要a_i的代价,问n个人按顺序填满m行(每个人可以填任意行)代价不超过b的方案数
简单的dp
while(n--){
int x=read();
for(int i=1;i<=m;i++){
for(int j=x;j<=b;j++){
(f[i][j]+=f[i-1][j-x])%=mod;
}
}
}
连通图问最多删除多少遍可以仍使得$distance(s1,t1)\le l1,distance(s2,t2)\le l2$
$1\le n,m \le 5\times 10^3$
bfs求出所有点对的最短路,两种情况即$s1 \to t1$和$s2\to t2$是否有重合的路径
所有情况取最大值即可
n个长m的串,$(1\le n,m \le 20)$修改每一个位置的串需要$v_{i,j}$的代价,问最小的代价使得每个串只要有一位满足它与其余串的该位都不相同
状压表示已经处理过的串,枚举状态每次只更改最小的没有被处理的串,对于该串,枚举每一位,要么更改这一位的字母,要么更改所有串这一位上与它字母相同的串并且保留更改代价最小的,这样的贪心可以使总代价最小
给定一颗无根树,树边可以黑白染色。对于选定的中心节点 $x$,一个优秀的染色方案应当满足树上任意一点到中心节点的路径上黑色的树边至多只有一个。
对于每个节点 $n$,求出以该节点作为中心节点时的染色方案数。
$n \leq 2e5$
考虑换根dp
当中心节点确定时,显然可以通过简单的树形DP解决。
方程为 $dp[x] = \prod(dp[son[x]] + 1)$
不难发现,当根节点移动时,仅会影响一条边上的两个节点的 $dp$ 数组,而这个可以 $O(1)$ 的进行转移。再模意义下 $dp[son[x]] + 1$ 的值可能为 $0$,此时逆元不存在,用前缀和后缀计算一下就可以了。
总复杂度 $O(n)$
+ 或 *
最多可以加一个括号,要求最大化表达式值。
乘号的数量不超过 15。
==== 解法 ====
这个题直接猜括号的两侧要么贴着头尾要么贴着乘号。
然后直接用 py 的 eval 就莽过去了 2333。
Code as follows:
<hidden><code>
S = input()
N = len(S)
Ans = eval(S)
for i in range(0, N, 2):
for j in range(i, N, 2):
if (i == 0 or (i > 0 and S[i - 1] == '*')) and (j == N - 1 or (j < N - 1 and S[j + 1] == '*')):
SS = S[:i] + str(eval(S[i:j+1])) + S[j+1:]
if int(eval(SS)) > Ans:
Ans = int(eval(SS))
print(Ans)
</code></hidden>
===== T - Kyoya and Permutation (553B) =====
* Solved by QwQ
==== 题意 ====
==== 解法 ====
===== U - Love Triangles (553C) =====
* Solved by QwQ
==== 题意 ====
==== 解法 ====
===== V - Nudist Beach (553D) =====
* Solved by QwQ
==== 题意 ====
==== 解法 ====
===== W - Case of Fugitive (555B) =====
* Solved by QwQ
==== 题意 ====
==== 解法 ====
===== X - Case of Chocolate (555C) =====
* Solved by Pantw
==== 题意 ====
给一个 $n\times n$ 尺寸的上三角方格。
有 $q$ 个操作。每次操作选择一个副对角线上的格子,指定一个方向,从副对角线上的格子开始对这个方向上的格子全部染色,直至碰见边界,或者遇见在之前的查询中染过色的格子为止。
对每个操作需要输出在该次操作中被染色的格子数。
示意图如下(图源题面):
==== 解法 ====
可以发现,经过一些操作后的剩余区域有以上几种形状。
每进行一次操作,可以看成将一个区域分为了两个这样的区域。
区域可由其右下角的斜边代表。
仔细观察可以发现,左上角的位置和右下角斜线的两个端点可以唯一确定一个这样的形状,右下角的第四种情况是 general case,其他三种均可看成这种形状的特例。
那么我们对每个区域需要保存的信息如下:
* $L$: 斜线左端点 $x$ 坐标
* $R$: 斜线右端点 $x$ 坐标
* $Lb$: left-bound,区域左边界对应的 $x$ 坐标
* $Ub$: upper-bound,区域上边界对应的 $y$ 坐标
直接包装成一个类,类里封装一个 Split 方法,在其中写出分裂时的行为即可。
每个区域用其右下角的斜线代表,这些斜线可以看成 $[1, n]$ 内的互不相交的闭区间。
我们可以用平衡树维护这样的区间信息,每次操作时取出相应区间将其一分为二,再插入回平衡树即可。
至于具体写法,考虑到 std::set 就是优秀的平衡树,我们直接用它来维护即可。
一些关键代码如下。
<hidden><code>
tuple<Interval, Interval, int> Split (int pos, Direction dir) const {
if(dir == UP)
return make_tuple(Interval(L, pos - 1, Lb, Ub), Interval(pos + 1, R, pos + 1, Ub), (N + 1 - pos) - Ub + 1);
else
return make_tuple(Interval(L, pos - 1, Lb, N + 1 - pos + 1), Interval(pos + 1, R, Lb, Ub), pos - Lb + 1);
}
bool operator < (const Interval &b) const {
return this→L == b.L ? this→Lb < b.Lb : this→L < b.L;
}
int main() {
S.insert(Interval(1, n, 1, 1));
for {
int x, y;
char c;
scanf(“%d%d %c”, &x, &y, &c);
Interval itv(x, x, INF, INF);
auto pos = S.insert(itv);
auto it = pos.first;
if(it == S.begin()) puts(“0”);
else {
–it;
if(it→R < x) puts(“0”);
else {
auto tup = it→Split(x, c == 'U' ? Interval::UP : Interval::LEFT);
S.erase(itv);
S.erase(it);
if(get<0>(tup).valid()) S.insert(get<0>(tup));
if(get<1>(tup).valid()) S.insert(get<1>(tup));
printf(“%d\n”, get<2>(tup));
}
}
}
}
</code></hidden>
===== Y - Case of a Top Secret (555D) =====
* Solved by Pantw
==== 题意 ====
平面上有横向一列 $n$ 个钉子,坐标 $x_1, \dots, x_n$。
给出 $m$ 组询问 $(a_i, l_i)$,询问如下:
初始时在编号为 $a_i$ 的钉子下方用长为 $l_i$ 的绳系一小重物,给予其向右的初速度。
在绳子与其他钉子重合时,绳子将绕过钉子,物体向回旋转。(角速度方向不变,横向速度方向改变)
示意图如下(图源题面):
钉子视为无限细,所以在钉子上打转不消耗绳长。问最后物体绕哪根单独的钉子不断旋转。
$1\le n, m\le 2\times 10^5$
$-10^9\le x_i\le 10^9$
==== 解法 ====
这个题初步的想法是直接模拟。
先将钉子排序,然后每次根据绳长、当前位置及运动方向在序列上二分下一个绕过的钉子。
这样做的一个最大问题就是时间。
可以很容易地构造出围着两个钉子转 $5\times 10^8$ 左右次的情况。
这样一来我们必须想办法加速我们的模拟过程。
如上图,假设我们的重物目前在左边的红色钉子,正向右摆,下一次要绕过的钉子是右边的红色钉子。三段长度如图所示标记。
那么当目前绳长 $l$ 满足 $l-2L<l_1$ 时,我们可以直接用 $l$ 除以 $L$ 的余数 $r$ 替代 $l$,并根据 $\lfloor \cfrac{l}{L}\rfloor$ 的奇偶性判断变换后的所在位置。若其为偶数,那么变换后在左侧的钉子;否则在右侧的钉子。
下证这么做是正确的。
首先右侧红色钉是下一次要绕过的钉,那么显然有 $L\le l<L+l_2$。
绕回时,绳剩余长度为 $l-L$。如果 $L \le l-L < L + l_1$,那么下一次将在左侧钉处绕向右。
那么整理一下式子,我们可以将 $l-2L<l_1$ 作为进入在左右侧红色钉之间循环的条件。当 $l<L$ 时,其自然退出这个循环。所以这样做是正确的。
这么做的时间复杂度应该是 $O(m\log n\log \max\limits_{i=1}^{n} |x_i|)$
具体证明目前没有想到比较简洁的证法,如果之后有了大概会补上。
===== Z - Case of Computer Network (555E) =====
* Solved by QwQ**
==== 题意 ====
==== 解法 ====
====== Comments ======