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
#include
#include
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
#include
#include
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的历程。
灿烂的明天正在等着你们!