垃圾ACMer的暑假训练220719
Codeforces
Codeforces Round #808 (Div. 2) D. Difference Array
原题指路:https://codeforces.com/contest/1708/problem/D
题意
给定一个长度为 n n n的非负整数序列 a a a,保证 a a a非降序排列.每次操作,构造一个新序列 b i = a i + 1 − a i ( 1 ≤ i ≤ n ) b_i=a_{i+1}-a_i\ \ (1\leq i\leq n) bi=ai+1−ai (1≤i≤n),并将 b b b非降序排列,将 b b b视为新的 a a a,序列长度变为 ( n − 1 ) (n-1) (n−1).进行 ( n − 1 ) (n-1) (n−1)次操作后,输出序列中剩下的一个整数.
有 t ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t (1≤t≤1e4)组测试数据.每组测试数据第一行输入整数 n ( 2 ≤ n ≤ 1 e 5 ) n\ \ (2\leq n\leq 1\mathrm{e}5) n (2≤n≤1e5).第二行输入 n n n个整数 a 1 , ⋯ , a n ( 0 ≤ a 1 ≤ ⋯ ≤ a n ≤ 5 e 5 ) a_1,\cdots,a_n\ \ (0\leq a_1\leq\cdots\leq a_n\leq 5\mathrm{e}5) a1,⋯,an (0≤a1≤⋯≤an≤5e5).数据保证所有测试数据的 n n n之和不超过 2.5 e 5 , a i 2.5\mathrm{e}5,a_i 2.5e5,ai之和不超过 5 e 5 5\mathrm{e}5 5e5.
对每组测试数据,输出进行 ( n − 1 ) (n-1) (n−1)次操作后序列中剩下的一个整数.
思路
设 S = ∑ i = 1 n a i \displaystyle S=\sum_{i=1}^n a_i S=i=1∑nai,则它在每次操作后会改变.进行一次操作后对 a a a升序排列,忽略所有为 0 0 0的项,注意到 n − 1 + a n ≤ S ( a i ≥ 1 ) n-1+a_n\leq S\ \ (a_i\geq 1) n−1+an≤S (ai≥1),即 n − 1 ≤ S − a n n-1\leq S-a_n n−1≤S−an恒成立.
进行一次操作后, S = a n − a 1 ≤ a n S=a_n-a_1\leq a_n S=an−a1≤an,则每次操作花费 O ( n log n ) O(n\log n) O(nlogn)的时间排序,并至少将 S S S减少 ( n − 1 ) (n-1) (n−1).第一次操作后, S = O ( a n ) S=O(a_n) S=O(an).总时间复杂度 O ( A log A ) O(A\log A) O(AlogA),其中 A = max { n , a n } A=\max\{n,a_n\} A=max{n,an},暴力模拟即可.
代码
const int MAXN = 1e5 + 5;
int a[MAXN];
int main() {
CaseT{
int n; cin >> n;
int pos = -1;
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (pos == -1 && a[i]) pos = i;
}
pos--; // 第一个非零数与0也要作差,故pos--
while (n > 1) {
for (int i = max(1, pos); i < n; i++) a[i] = a[i + 1] - a[i];
n--;
sort(a + pos, a + n + 1);
pos = upper_bound(a + 1, a + n + 1, 0) - (a + 1);
if (pos == n - 1) break;
}
cout << a[n] << endl;
}
}
Codeforces Round #809 (Div. 2) C. Qpwoeirut And The City
原题指路:https://codeforces.com/contest/1706/problem/C
题意
编号 1 ∼ n 1\sim n 1∼n的楼排成一行,其中第 i i i座楼的高度为 h i h_i hi.称一座楼是cool的,如果它的高度严格大于其左右的楼的高度,显然首尾的楼不能是cool的.现可选择一些楼增加整数的高度,使得cool的楼最多,求增加的高度之和的最小值.
有 t ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t (1≤t≤1e4)组测试数据.每组测试数据第一行输入整数 n ( 3 ≤ n ≤ 1 e 5 ) n\ \ (3\leq n\leq 1\mathrm{e}5) n (3≤n≤1e5).第二行输入 n n n个整数 h 1 , ⋯ , h n ( 1 ≤ h i ≤ 1 e 9 ) h_1,\cdots,h_n\ \ (1\leq h_i\leq 1\mathrm{e}9) h1,⋯,hn (1≤hi≤1e9).数据保证所有测试数据的 n n n之和不超过 2 e 5 2\mathrm{e}5 2e5.
对每组测试数据,输出使得cool的楼最多时增加的高度之和的最小值.
思路
显然cool的楼最多有 ⌊ n − 1 2 ⌋ \left\lfloor\dfrac{n-1}{2}\right\rfloor ⌊2n−1⌋座,且 n n n为奇数时方案唯一,每两座cool的楼间相隔 1 1 1座楼.
n n n为偶数时,可能会出现两座cool的楼间相隔 2 2 2座楼的情况.注意到最后一个cool的楼只能是第 ( n − 1 ) (n-1) (n−1)或 ( n − 2 ) (n-2) (n−2)座, d p [ i ] dp[i] dp[i]表示楼 i i i是最后一个cool的楼的最小代价,则 a n s = min { d p [ n − 1 ] , d p [ n − 2 ] } ans=\min\{dp[n-1],dp[n-2]\} ans=min{dp[n−1],dp[n−2]}.
从第 2 2 2座楼开始,偶数号的cool楼只能与前面的楼相隔 1 1 1座楼,而下标为奇数号的cool楼能与前面的楼相隔 1 1 1或 2 2 2座楼.
代码
int main() {
CaseT{
int n; cin >> n;
vl h(n + 1);
for (int i = 1; i <= n; i++) cin >> h[i];
ll ans = 0;
if (n & 1) {
for(int i = 2; i <= n; i += 2) ans += max((ll)0, max(h[i - 1], h[i + 1]) - h[i] + 1);
}
else {
vl dp(n + 1); // dp[i]表示楼i是最后一个cool的最小代价
for (int i = 2; i < n; i++) {
int tmp = max((ll)0, max(h[i - 1], h[i + 1]) - h[i] + 1);
if (i & 1) dp[i] = min(dp[i - 2], dp[i - 3]) + tmp; // 奇数位,与当前隔1、2取min
else dp[i] = dp[i - 2] + tmp; // 偶数位只能隔1
}
ans = min(dp[n - 1], dp[n - 2]); // 最后一个cool的楼只能是n-1或n-2
}
cout << ans << endl;
}
}
Codeforces Round #809 (Div. 2) D1. Chopping Carrots (Easy Version)
原题指路:https://codeforces.com/contest/1706/problem/D1
题意 ( 4 s ) (4\ \mathrm{s}) (4 s)
给定一个长度为 n n n的整数序列 a 1 , ⋯ , a n a_1,\cdots,a_n a1,⋯,an和整数 k k k,定义一个整数序列 p 1 , ⋯ , p n ( 1 ≤ p i ≤ k ) p_1,\cdots,p_n\ \ (1\leq p_i\leq k) p1,⋯,pn (1≤pi≤k)的价值为 max 1 ≤ i ≤ n ⌊ a i p i ⌋ − min 1 ≤ i ≤ n ⌊ a i p i ⌋ \displaystyle\max_{1\leq i\leq n}\left\lfloor\dfrac{a_i}{p_i}\right\rfloor-\min_{1\leq i\leq n}\left\lfloor\dfrac{a_i}{p_i}\right\rfloor 1≤i≤nmax⌊piai⌋−1≤i≤nmin⌊piai⌋.
有 t ( 1 ≤ t ≤ 100 ) t\ \ (1\leq t\leq 100) t (1≤t≤100)组测试数据,每组测试数据第一行输入整数 n , k ( 1 ≤ n , k ≤ 3000 ) n,k\ \ (1\leq n,k\leq 3000) n,k (1≤n,k≤3000),第二行输入 n n n个整数 a 1 , ⋯ , a n ( 1 ≤ a 1 ≤ a 2 ≤ ⋯ ≤ 3000 ) a_1,\cdots,a_n\ \ (1\leq a_1\leq a_2\leq\cdots\leq 3000) a1,⋯,an (1≤a1≤a2≤⋯≤3000).数据保证所有测试数据的 n n n之和不超过 3000 3000 3000.
对每组测试数据,输出序列 p p p的最小价值.
思路
注意到 v = min 1 ≤ i ≤ n ⌊ a i p i ⌋ \displaystyle v=\min_{1\leq i\leq n}\left\lfloor\dfrac{a_i}{p_i}\right\rfloor v=1≤i≤nmin⌊piai⌋当 p i > a i p_i>a_i pi>ai时取 0 0 0,而序列 a a a升序,故只需枚举 v v v从 0 0 0到 a 1 a_1 a1,构造一个 min 1 ≤ i ≤ n ⌊ a i p i ⌋ = v \displaystyle \min_{1\leq i\leq n}\left\lfloor\dfrac{a_i}{p_i}\right\rfloor=v 1≤i≤nmin⌊piai⌋=v的序列 p : p i = min { k , ⌊ a i v ⌋ } ( v ≠ 0 ) p:p_i=\min\left\{k,\left\lfloor\dfrac{a_i}{v}\right\rfloor\right\}\ \ (v\neq 0) p:pi=min{k,⌊vai⌋} (v=0);若 v = 0 v=0 v=0,取 p i = k p_i=k pi=k.此时 1 ≤ p i ≤ k , v ≤ ⌊ a i p i ⌋ 1\leq p_i\leq k,v\leq\left\lfloor\dfrac{a_i}{p_i}\right\rfloor 1≤pi≤k,v≤⌊piai⌋, a n s ans ans即所有 max 1 ≤ i ≤ n ⌊ a i p i ⌋ − v \displaystyle\max_{1\leq i\leq n}\left\lfloor\dfrac{a_i}{p_i}\right\rfloor-v 1≤i≤nmax⌊piai⌋−v的最小值.时间复杂度 O ( n ⋅ a 1 ) O(n\cdot a_1) O(n⋅a1).
代码
int main() {
CaseT{
int n,k; cin >> n >> k;
vi a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
if (n == 1) {
cout << 0 << endl;
continue;
}
int maxnum = -INF, minnum = INF;
for (int i = 1; i <= n; i++) { // v=0时,取p_i=k
maxnum = max(maxnum, a[i] / k);
minnum = min(minnum, a[i] / k);
}
int ans = maxnum - minnum;
for (int v = 1; v <= a[1]; v++) {
maxnum = -INF, minnum = INF;
for (int i = 1; i <= n; i++) {
int p = min(k, a[i] / v);
maxnum = max(maxnum, a[i] / p);
minnum = min(minnum, a[i] / p);
}
ans = min(ans, maxnum - minnum);
}
cout << ans << endl;
}