2021 高階競程 Contest #4 - 題解
- Task Provider: leo
- Task Setter: leo
首殺 team21_25 (00:02)
解法說明
整數的 xor 運算符合以下條件,下方以「」當作 xor 運算符:
- 交換律:
- 結合律:
- 單位元為 :
- 反元素為自身:
解法 1
可以注意到 xor 有結合律的特性,因此可以直接用線段樹維護,
建造 O(),單次查詢 ,
總複雜度 。
參考程式
解法 2
先思考區間合的問題,在解區間和問題時可以預處理前綴和,
在此假設預處理完的前綴和陣列名字叫做
每次查詢 的和時,只要輸出 即可。
前綴和這個做法利用了上述四種特性。
而 xor 也有這種特性,因此可以預處理前綴 xor 的值,
每次查詢輸出 即可。
預處理 ,單次查詢 ,總複雜度
標準程式
- Task Provider: D4nnyLee
- Task Setter: D4nnyLee
首殺 team21_12 (00:28)
解法說明
定義一個新的長度為 的陣列 ,而 。
解法 1
考慮以下兩個陣列:
題目要求的答案其實就是上面兩個陣列的最大連續和的最大值。
第 個陣列的最大連續和就是所有 為奇數的 代入 後的最大值,
以此類推,第 個陣列的最大連續和就是 為偶數的最大值,因此兩個取最大值就是所有可能的 的最大值。
解法 2
定義 是所有以 為開頭的區間代入 後可以得到的最小值,而 就是最大值。
則我們可以得到以下關係式:
最後的答案就是 。
標準程式
解法 1
解法 2
- Task Provider: ys
- Task Setter: ys
首殺 team21_24 (01:21)
解法說明
將一段區間從行程表中移除,就只剩下前綴及後綴的行程表
正好,題目要求的就是前後綴相加期間變化的最高及最低值
對於區間 算出 以前的前綴和極值,以及 之後的後綴和極值
接著處理這些極值就能得到題目所要求的答案
定義 為以 結尾的前綴和,則 且
- 定義 為以 結尾的最小前綴和
則 且
- 定義 為以 結尾的最大前綴和
則 且
後綴和極值也能以動態規劃的方式去思考:
因為後綴和是以 開始累加的,所以該上界為
因為後綴和是以 開始累加的,所以該下界為
標準程式
- Task Provider: raiso
- Task Setter: raiso
首殺 team21_12 (02:00)
解法說明
首先考慮討論團彼此之間不會交錯,也就是最終的討論團順序屬於三個字串的共同子序列。
在上課的時候我們有學到如何解決兩個字串的共同子序列,這時候就可以參考他的 DP 轉移式的設計方式,相同時 ,不同時找附近最大值繼承。
這題是 3 個字串的 LCS 題目,不過有卡記憶體用量,並不能開 個狀態。需要壓縮到 的狀態數量才會過。
觀察可以發現固定第一行的某元素下去找同字母的 LCS 對於第一行而言,只會用到當前跟上一個的值,所以不用存整條的 DP 式,因此 的時候就可以把前面的 clear 掉
標準程式
- Task Provider: arashi87
- Task Setter: arashi87
首殺 team21_15 (01:31)
解法說明
我們可以根據題序很簡單的想到這是一題 dp,但是因為有單點修改以及變相區間求和所以需要加上線段樹。
我們可以在線段樹上每個節點都開一個 dp 數組,對於每個 root 節點維護一個 dp 數組,,且 ,其代表意義為 root 節點的左節點選 (不選),右節點選(不選),給定初值只要對於每個葉節點進行 就行。
因為相鄰兩個節點不能被同時選中,所以在進行 pushup 回溯時要注意不能出現像是 這種,接下來我們就可以推出下面一個很長的轉移式。
其實這可以直接合併成下列轉移式。
然後因為這長度有點令人崩潰,所以我將後面兩維壓成一維,然後將其視為二進位轉換成十進位。
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
最後我們只要取個根節點的 std::max(std::max(seg[1][0], seg[1][1]), std::max(seg[1][2], seg[1][3]))
就能得到答案了。
標準程式
#include <algorithm>
#include <stdio.h>
#define int long long
#define lc (rt << 1)
#define rc (rt << 1 | 1)
const int maxN = 2e5 + 7;
int n, m, arr[maxN];
int seg[maxN << 2][5], ans = 0;
inline int max(int a, int b, int c)
{
return std::max(a, std::max(b, c));
}
void pushup(int rt)
{
seg[rt][0] = max(seg[lc][0] + seg[rc][0], seg[lc][1] + seg[rc][0], seg[lc][0] + seg[rc][2]);
seg[rt][1] = max(seg[lc][0] + seg[rc][1], seg[lc][1] + seg[rc][1], seg[lc][0] + seg[rc][3]);
seg[rt][2] = max(seg[lc][2] + seg[rc][0], seg[lc][2] + seg[rc][2], seg[lc][3] + seg[rc][0]);
seg[rt][3] = max(seg[lc][2] + seg[rc][1], seg[lc][3] + seg[rc][1], seg[lc][2] + seg[rc][3]);
}
void build(int rt, int L, int R)
{
if (L == R)
seg[rt][3] = arr[L];
else {
int mid = (L + R) >> 1;
build(rt << 1, L, mid);
build(rt << 1 | 1, mid + 1, R);
pushup(rt);
}
}
void modify(int rt, int L, int R, int x, int d)
{
if (L == R)
seg[rt][3] = d;
else {
int mid = (L + R) >> 1;
if (x <= mid)
modify(rt << 1, L, mid, x, d);
else
modify(rt << 1 | 1, mid + 1, R, x, d);
pushup(rt);
}
}
signed main()
{
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n; i++)
scanf("%lld", arr + i);
build(1, 1, n);
for (int i = 1, x, y; i <= m; i++) {
scanf("%lld%lld", &x, &y);
modify(1, 1, n, x, y);
printf("%lld\n", std::max(std::max(seg[1][0], seg[1][1]), std::max(seg[1][2], seg[1][3])));
}
}