CSP-S 2019第二輪第二試昨天上午正式結束了,賽後仔細拜讀了DAY2的三個題目,難!但也是明年NOI選手不錯的試煉機會,高分選手大概率也是明年NOI賽場上的選手。
第一題:Emiya 家今天的飯,比較難的動態規劃
第二題:劃分,動態規劃,高精度,單調隊列
第三題:樹的重心,dfs序,線段樹
以下代碼均在oitiku測試,第一題Emiya 家今天的飯,O(n^2m)的算法僅得到84分,超時了4個點,理論上應該是可以通過的:)第二題劃分,由於數據太大,寫了高精度,寫的不是太好,最後三個點在oitiku上MLE了,僅得到88分,之後再來改進:)第三題僅寫了一下大致想法,往後再來補坑
CSP-S 2019 D2T1 Emiya 家今天的飯
思路:容斥,方案計數,對998, 244, 353取餘數,已經在預示着這題是動態規劃了。先考慮無限制情況下,顯然是一個普及組難度的dp問題,然後考慮k/2的限制,我們可以從總的方案中減去不符合k/2限制的方案數即可。
代碼如下:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 210;
const int maxm = 2100;
const long long mod = 998244353;
long long g[maxn][maxn], f[maxn][maxn], a[maxn][maxm], line[maxn];
int n, m;
int main() {
freopen("meal.in", "r", stdin);
freopen("meal.out", "w", stdout);
cin >> n >> m;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) {
cin >> a[i][j];
a[i][j] %= mod;
line[i] = (line[i] + a[i][j]) % mod;
}
long long sum = 0;
g[0][0] = 1;
for (int i = 1; i <= n; ++i)
for (int j = 0; j <= i; ++j) {
g[i][j] = g[i - 1][j];
for (int k = 1; k <= m; ++k) {
if (j > 0)
g[i][j] += g[i - 1][j - 1] * a[i][k] % mod;
g[i][j] %= mod;
}
}
for (int i = 1; i <= n; ++i)
sum = (sum + g[n][i]) % mod;
for (int l = 1; l <= m; ++l) {
memset(f, 0, sizeof(f));
// 差值可能爲負數,偏移量爲n + 1
f[0][n + 1] = 1;
for (int i = 1; i <= n; ++i)
for (int j = n + 1 - i; j <= i + n + 1; ++j) {
f[i][j] = f[i - 1][j];
f[i][j] += (f[i - 1][j - 1] * a[i][l]) % mod;
f[i][j] += (f[i - 1][j + 1] * ((line[i] - a[i][l] + mod) % mod)) % mod;
f[i][j] %= mod;
}
for (int i = 1; i <= n; ++i) {
sum = (sum - f[n][i + n + 1]) % mod;
if (sum < 0)
sum += mod;
}
}
cout << sum << endl;
fclose(stdin);
fclose(stdout);
return 0;
}
CSP-S 2019 D2T2 劃分
思路:根據數據規模顯然是要一個O(n)的算法,粗略的就是先貪心,然後典型的序列型動態規劃。dp一下n^2,必須使用單調隊列優化至O(n)。最後由於數據太大,需要用高精度或者別的取巧的方法優化一下,我們這裏使用傳統的高精度來寫一下:)
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 4e7, maxm = 1e5;
int n, type, x, y, z, m;
int a[maxn], p[maxm], l[maxm], r[maxm], b[maxn + 1], q[maxn + 1], head, tail;
long long sum[maxn + 1];
struct BigInteger {
int len, data[50];
BigInteger(string& s) {
memset(data, 0, sizeof(data));
len = s.size();
for (int i = 0; i < len; ++i)
data[i] = s[len - i - 1] - '0';
}
void set(long long num) {
len = 0;
memset(data, 0, sizeof(data));
while (num > 0) {
data[len] = num % 10;
len ++;
num /= 10;
}
len ++;
}
BigInteger() {
len = 0;
memset(data, 0, sizeof(data));
}
BigInteger operator + (const BigInteger &b) {
BigInteger c;
c.len = 0;
while(c.len <= max(len, b.len) + 1) {
c.data[c.len] += data[c.len] + b.data[c.len];
c.data[c.len + 1] += c.data[c.len] / 10;
c.data[c.len] %= 10;
c.len ++;
}
while (c.len > 0 && c.data[c.len] == 0)
c.len --;
c.len++;
return c;
}
BigInteger operator * (const BigInteger &b) {
BigInteger c;
c.len = len + b.len - 1;
for (int i = 0; i < len; ++i)
for (int j = 0; j < b.len; ++j)
c.data[i + j] += data[i] * b.data[j];
for (int i = 1; i < c.len; ++i) {
c.data[i] += c.data[i - 1] / 10;
c.data[i - 1] %= 10;
}
while (c.data[c.len - 1] >= 10) {
c.data[c.len] = c.data[c.len - 1] / 10;
c.data[c.len - 1] %= 10;
c.len += 1;
}
return c;
}
string toString() {
string s = "";
for (int i = len - 1; i >= 0; --i)
s += (char)(data[i] + '0');
return s;
}
};
int main() {
freopen("partition.in", "r", stdin);
freopen("partition.out", "w", stdout);
cin >> n >> type;
if (type == 0) {
for (int i = 0; i < n; ++i)
cin >> a[i];
} else {
cin >> x >> y >> z >> a[0] >> a[1] >> m;
for (int i = 2; i < n; ++i)
a[i] = (1LL * x * a[i - 1] + 1LL * y * a[i - 2] + z) & 0x3fffffff;
for (int i = 0; i < m; ++i)
cin >> p[i] >> l[i] >> r[i];
for (int i = 0, j = 0; i < n; ++i) {
if (i == p[j]) ++j;
a[i] = a[i] % (r[j] - l[j] + 1) + l[j];
}
}
for (int i = 0; i < n; ++i)
sum[i + 1] = sum[i] + a[i];
q[tail++] = 0;
for (int i = 0; i < n; ++i) {
while (head + 1 < tail && 2 * sum[q[head + 1]] - sum[b[q[head + 1]]] <= sum[i + 1])
++head;
b[i + 1] = q[head];
while (head < tail && 2 * sum[i + 1] - sum[b[i + 1]] <= 2 * sum[q[tail - 1]] - sum[b[q[tail - 1]]])
--tail;
q[tail++] = i + 1;
}
BigInteger ans, tmp;
for (int i = n; i > 0; i = b[i]) {
tmp.set((long long)(sum[i] - sum[b[i]]));
tmp = tmp * tmp;
ans = ans + tmp;
}
cout << ans.toString() << endl;
fclose(stdin);
fclose(stdout);
return 0;
}
CSP-S D2T3 樹的重心
思考:
https://codeforces.com/problemset/problem/686/D,恰好比賽前跟同學們也討論過子樹重心的問題。
樹的問題的確可以深挖很多有趣的性質,這也是我比較喜歡樹上的問題的一些原因。這可能也是爲什麼樹的題目出那麼多的原因之一吧。逆向思維一下,如果這點是樹的重心,那麼一定是刪除size最大的子樹裏面的邊。本質dfs序,然後用線段樹維護即可。
2019賽季告一段落,高潮已過,停課衝刺的同學們該調整心情迴歸課堂了,衝刺明年NOI的同學,革命尚未成功,同志仍需努力!
每一位同學都值得爲這次CSP-J/S賽寫一個總結,心得,感悟,亦或者是回顧自己的學習C++/算法競賽、OI的歷程。
燦爛的明天正在等着你們!