曾经做过的一些小水题, 总之我是不会做.

这是最后一篇, 第3篇.

Topcoder SRM 744(Div.1 + Div.2)

解题口胡报告 + 吐槽.

发现其实SRM的题没有很不可做. 不过感觉赛时肯定还是会爆负, 让我75min想到写完这些题是做不到.

Div.2 250

入门题, 把一个左开右闭区间分成三个左开右闭区间.

没写, 除一除加一加就好了.

Div.2 500

入门题, 问一个区间里有多少数是完全平方数且满足各数位的数字是波动的.

我居然天真地想数位DP

数据范围不大, 反正枚举平方数再检查是够的.

Div.2 1000 / Div.1 250

有一个$N \times M$矩阵, 格子$(i,j)$的权值是$\max{(i,j)} \mod{3}$. 求一个给定子矩阵的权值和. $N, M \le 10^9$.

什么嘛矩阵是从0开始存的?

转成二维前缀和. 就讨论一下对角线, 对角线两边, 和较大的那一维多出来的一块, 加起来就行.

ModularQuadrant.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int summ[3] = {0, 1, 3};
const int val[3] = {3, 1, 2};

inline ll seq(int init, int count) {
ll tail = (ll)init + 3ll * (count - 1);
return (init + tail) * count / 2ll;
}

class ModularQuadrant {
public:
ll sum(int, int, int, int);
ll prefix(int, int);
};

ll ModularQuadrant::sum(int r1, int c1, int r2, int c2) {
return ModularQuadrant::prefix(r2, c2) -
ModularQuadrant::prefix(r2, c1 - 1) -
ModularQuadrant::prefix(r1 - 1, c2) +
ModularQuadrant::prefix(r1 - 1, c1 - 1);
}
ll ModularQuadrant::prefix(int r, int c) {
if (r == -1 || c == -1) return 0;
int lim = min(r, c) + 1;
ll ang = (lim / 3) * 3 + summ[lim % 3];
ll tri = seq(1, lim / 3) * 1ll + seq(2, lim / 3) * 2ll + seq(0, lim / 3) * 0;
if (lim % 3 == 1) {
tri += (lim - 1) * 0;
} else if (lim % 3 == 2) {
tri += 1ll * (lim - 1) + 0ll * (lim - 2);
}
tri *= 2ll;
if (r == c) return tri + ang;
ll ret = 0; int bgn = lim, eed = max(r, c), len = eed - bgn + 1;
ret += (len / 3) * 1ll * lim + (len / 3) * 2ll * lim + (len / 3) * 0ll * lim;
if (len % 3 == 1) {
ret += 1ll * val[bgn % 3] * lim;
} else if (len % 3 == 2) {
ret += 1ll * val[bgn % 3] * lim + 1ll * val[(bgn + 1) % 3] * lim;
}
return ret + ang + tri;
}

Div.1 500

给一个01矩阵, 每次可以操作一个$(i,j)$, 将$i$行和$j$列除了她自己以外的格子都取反.

问有没有可能把全部格子都变成1. $N, M \le 2000$.

这就是比较经典的套路.

首先观察一下操作, 发现相当于把行取反再把列取反. 这和我们见过的题相比多出的限制就是行列操作次数必须相等, 只要最后查一下就行了.

枚举第一行反或不反, 把后面的行都和她对齐, 然后看第一行还有哪些是0, 就要对列取反. 把次数统计起来.

UniformingMatrix.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <bits/stdc++.h>

using namespace std;

const string YES = "Possible";
const string NO = "Impossible";

int line[200];

class UniformingMatrix {
public:
string isPossible(vector<string> M);
};

string UniformingMatrix::isPossible(vector<string> M) {
int n = M.size(), m = M[0].size();
bool sol = true; int row = 0, col = 0;
for (int i = 0; i < m; ++i)
line[i] = M[0][i] - '0';
for (int i = 1; i < n; ++i) {
int lst = line[0] ^ (M[i][0] - '0');
for (int j = 1; j < m; ++j) {
if ((M[i][j] - '0') ^ line[j] != lst) {
sol = false;
break;
}
}
if (sol == false) break;
if (lst) row++;
}
if (sol) {
for (int i = 0; i < m; ++i)
if (!line[i]) col++;
if (row == col) return YES;
}
for (int i = 0; i < m; ++i)
line[i] = (1 ^ (M[0][i] - '0'));
row = 1; col = 0;
for (int i = 1; i < n; ++i) {
int lst = line[0] ^ (M[i][0] - '0');
for (int j = 1; j < m; ++j) {
if ((M[i][j] - '0') ^ line[j] != lst) {
sol = false;
break;
}
}
if (sol == false) break;
if (lst) row++;
}
if (sol) {
for (int i = 0; i < m; ++i)
if (!line[i]) col++;
if (row == col) return YES;
}
return NO;
}

Div.1 1000

给一颗有根树. 每个点有需求和花费. 给每个点赋值使得每个点到根的路径上的权值和不小于需求, 代价是值乘以点权.

最小化花费. $N \le 10^5$.

TC题正解总是用匪夷所思的简单做法我居然想要DP

自下而上地贪心考虑. 我们的目的首先是满足权值下限, 先不考虑祖先(后面会更新). 考虑子树中所有和当前节点路径上权值和为0(不包括端点)的节点. 做到这里时他们的要求肯定是满足的. 那么我们可以考虑, 如果减小他们的权值, 而在当前节点增加同样的权值, 如果子节点的费用和大于当前节点的费用, 这一步调整就是优的.

如何快速做这个过程呢? 可以用小根堆来维护各点权值. 初始先将儿子压入堆. 然后做操作. 当堆顶被减为0时, 把堆顶节点的堆和当前堆合并. 为了保证复杂度要启发式合并. (感觉左偏树也是可以的).

然后还有一些细节, 大概就是每次减小量的上界还有满足条件就退出之类的, 代码里都写了.

还有这鬼畜的加密读入

CoverTreePaths.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int maxn = 1e+5 + 5;

struct edge {
int to, nxt;
}e[maxn];
struct node {
ll val; int id;
node(ll v_ = 0, int i_ = 0) : val(v_), id(i_) {}
inline bool operator<(const node &rhs) const {
return (val == rhs.val) ? (id > rhs.id) : (val > rhs.val);
}
};
int f[maxn], cs[maxn], nd[maxn];
ll sumcs[maxn], sub[maxn];
int ptr, lnk[maxn];
int v[maxn];
priority_queue<node> q[maxn];

inline void add(int bgn, int end) {
e[++ptr] = (edge){end, lnk[bgn]};
lnk[bgn] = ptr;
}
void merge(int frm, int to) {
while (q[frm].size()) {
node u = q[frm].top(); q[frm].pop();
u.val -= sub[frm];
u.val += sub[to];
q[to].push(u);
}
}

class CoverTreePaths {
public:
ll minimumCost(int,
vector<int>, vector<int>, vector<int>, vector<int>);
};

ll CoverTreePaths::minimumCost(int n,
vector<int> p, vector<int> d,
vector<int> c, vector<int> params) {
// PreProcess
for (vector<int>::size_type i = 0; i < p.size(); ++i) {
f[i + 1] = p[i];
add(f[i + 1], i + 1);
}
int bgn = p.size();
for (int i = bgn; i < n - 1; ++i) {
p.push_back((1ll * params[0] * p[i - 1] + params[1]) % (i + 1));
f[i + 1] = p[i];
add(f[i + 1], i + 1);
}
for (vector<int>::size_type i = 0; i < d.size(); ++i) {
nd[i] = d[i];
}
bgn = d.size();
for (int i = bgn; i < n; ++i) {
d.push_back((1ll * params[2] * d[i - 1] + params[3]) % params[4] + 1);
nd[i] = d[i];
}
for (vector<int>::size_type i = 0; i < c.size(); ++i) {
cs[i] = c[i];
}
bgn = c.size();
for (int i = bgn; i < n; ++i) {
c.push_back((1ll * params[5] * c[i - 1] + params[6]) % params[7] + 1);
cs[i] = c[i];
}

// SolvingProblem
ll ans = 0;
for (int k = n - 1; ~k; --k) {
sumcs[k] = 0;
sub[k] = 0;
for (int p = lnk[k]; p; p = e[p].nxt) {
int y = e[p].to;
sumcs[k] += cs[y];
q[k].push(node(v[y], y));
}
while (1) {
ll delta = 0;
if (!q[k].size()) {
if (v[k] >= nd[k]) {
break;
} else delta = nd[k] - v[k];
} else if (cs[k] < sumcs[k]) {
delta = q[k].top().val - sub[k];
} else if (v[k] < nd[k]) {
delta = min((ll)nd[k] - v[k], q[k].top().val - sub[k]);
} else break;

ans += delta * cs[k];
ans -= delta * sumcs[k];
sub[k] += delta;
v[k] += delta;

vector<node> waitList;
while (q[k].size() && q[k].top().val == sub[k]) {
node u = q[k].top(); q[k].pop();
waitList.push_back(u);
sumcs[k] -= cs[u.id];
}

for (auto u : waitList) {
sumcs[k] += sumcs[u.id];
if (q[u.id].size() <= q[k].size()) {
merge(u.id, k);
} else {
merge(k, u.id);
q[k] = q[u.id];
sub[k] = sub[u.id];
}
}
}
}
return ans;
}

没啦.