T1
给定 n n n 个数 { a n } \{a_n\} {an},每次操作可以选择一个 i ∈ [ 1 , n ] i\in [1,n] i∈[1,n] 使得 a i ← a i − 1 a_i\gets a_i-1 ai←ai−1。求最少操作次数使得 a a a 中除了 0 0 0 以外其他值至多出现 1 1 1 次。
n ≤ 2 × 1 0 5 n\le 2\times 10^5 n≤2×105, a i ∈ [ 0 , 1 0 6 ] a_i\in [0,10^6] ai∈[0,106]。
直接在值域上考虑,令 t i t_i ti 表示值为 i i i 的元素个数,从大往小考虑,若 t i > 1 t_i>1 ti>1 那么需要执行 t i − 1 t_i-1 ti−1 次操作令 t i ← 1 t_i\gets 1 ti←1,与此同时会令 t i − 1 ← t i − 1 + t i − 1 t_{i-1}\gets t_{i-1}+t_i-1 ti−1←ti−1+ti−1;一直这么推下去推到 0 0 0 为止即可。如果值域大一些直接考虑有值的 n n n 个位置即可。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define open(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
const int maxn = 1e6 + 5;
ll t[maxn]; int n, a[maxn];
int main() {
open(seq);
scanf("%d", &n);
for (int i = 1; i <= n; i ++)
scanf("%d", &a[i]), t[a[i]] ++;
ll ans = 0;
for (int i = 1e6; i > 0; i --)
if (t[i] > 1)
ans += t[i] - 1, t[i - 1] += t[i] - 1;
printf("%lld\n", ans);
return 0;
}
T2
给定 n n n,构造一个仅包含 X , Y , D \texttt{X},\texttt{Y},\texttt{D} X,Y,D 三种字符的字符串使得 XYD \texttt{XYD} XYD 这个子序列恰好出现 n n n 次。要求串长 ≤ 6000 \le 6000 ≤6000。
n ≤ 1 0 9 n\le 10^9 n≤109。
赛时糖丸了,推了一个关于平方的做法,差点就被我发现了。一个简单的做法是考虑在 X \texttt{X} X 后面放 YD \texttt{YD} YD 的贡献,发现若放了 n n n 个则产生了 n ( n + 1 ) 2 \cfrac{n(n+1)}{2} 2n(n+1) 个 XYD \texttt{XYD} XYD 子序列(可能往上往下有出入,大概长这样),于是我们考虑把 n n n 拆成这么几段,先放一个由若干段 YD \texttt{YD} YD 循环组成的串,然后在对应位置插入若干个 X \texttt{X} X 即可。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2005;
#define ll long long
#define open(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
ll a[maxn];
int main() {
open(xyd);
int n, m; scanf("%d", &n);
for (m = 1; m <= 2000; m ++)
if (a[m - 1] + m > n) break;
else a[m] = a[m - 1] + m;
for (m --; m > 0; m --, putchar('Y'), putchar('D'))
for (; a[m] <= n; n -= a[m]) putchar('X');
return 0;
}
T3
给定两个长度为 n n n 的序列 x , y x,y x,y,再给定 m m m 个长度为 n n n 的序列,对于第 i i i 个序列 a i a_i ai 选择以下操作之一进行:
- 对于所有 j ∈ [ 1 , n ] j\in[1,n] j∈[1,n],令 x j ← max ( x j , a i , j ) x_j\gets \max(x_j,a_{i,j}) xj←max(xj,ai,j)。
- 对于所有 j ∈ [ 1 , n ] j\in[1,n] j∈[1,n],令 y j ← max ( y j , a i , j ) y_j\gets \max(y_j,a_{i,j}) yj←max(yj,ai,j)。
要求按顺序操作,求操作后最大的 ∑ i = 1 n x i + y i \sum^n_{i=1}x_i+y_i ∑i=1nxi+yi。
n ≤ 10 n\le 10 n≤10, m ≤ 1 0 5 m\le 10^5 m≤105。
先拼了一档 m ≤ 20 m\le 20 m≤20 的暴搜做法,然后又拼了一个乱搞做法:每次比较 x , y x,y x,y 产生的增量,哪个多操作哪个,最终骗到 72 p t s 72\mathrm{pts} 72pts。然后一种可行解是随机化,正确率还挺高。
T4
显然最小的取法就是从每个集合中取一个最小值求和,我们设这个最优值为 a n s ans ans。考虑其可能的后几个略小一些的状态。我们将每个集合按照次小值减最小值的差从小到大排序,每个集合内部按从小到大排序。假设当前状态 { i , j , w } \{i,j,w\} {i,j,w} 表示枚举到第 i i i 个集合中的第 j j j 个数,考虑将 a n s ans ans 中第 i i i 个集合取的数更换为 j j j,更换后的答案为 w w w。令 c ( i , j ) c(i,j) c(i,j) 表示集合 i i i 中第 j j j 大的值,最初始的状态即为 { 1 , 2 , a n s − c ( 1 , 1 ) + c ( 1 , 2 ) } \{1,2,ans-c(1,1)+c(1,2)\} {1,2,ans−c(1,1)+c(1,2)}。对于每个状态会有三个可能的分支:
- 如果 j < s i z i j<siz_i j<sizi,则一种可能的选择即为放弃更换为 j j j,考虑更换为 j + 1 j+1 j+1。新的状态为 { i , j + 1 , w − c ( i , j ) + c ( i + j ) } \{i,j+1,w-c(i,j)+c(i+j)\} {i,j+1,w−c(i,j)+c(i+j)}。
- 如果
i
i
i 不是最后一个集合:
- 一种可能的选择即为决定更换为 j j j,开始考虑 i + 1 i+1 i+1 集合的选择。新的状态为 { i + 1 , 2 , w − c ( i + 1 , 1 ) + c ( i + 1 , 2 ) } \{i+1,2,w-c(i+1,1)+c(i+1,2)\} {i+1,2,w−c(i+1,1)+c(i+1,2)}。
- 另外,如果 j = 2 j=2 j=2,则一种可能的选择即为放弃更换 i i i 中的元素,直接考虑 i + 1 i+1 i+1 集合的选择。新的状态为 { i + 1 , 2 , w − c ( i , 2 ) + c ( i , 1 ) − c ( i + 1 , 1 ) + c ( i + 1 , 2 ) } \{i + 1,2,w - c(i,2) + c(i,1) - c(i+1,1) + c(i+1,2)\} {i+1,2,w−c(i,2)+c(i,1)−c(i+1,1)+c(i+1,2)}。
开一个优先队列记录这些状态,从中取 k k k 轮即为答案。