《如 何 速 通 一 套 题》2
A lock
死因:情况考虑不完全,可以舍近求远。
正解:
正解 1
加上舍近求远的情况就可以了。
正解 2
把每一位需要变化的数量处理出来,此时就变成了从 0000
开始破解的问题。
\(10^4\) 个状态,BFS 预处理。
\(O(N) - O(N)\),其中 \(N\) 是位数,这里 \(N = 4\)。
code
正解 2:
#include <bits/stdc++.h>
using namespace std;
int t, d[6], dis[10][10][10][10];
string a, b;
queue<pair<string, int> > q;
string inc(string x, int l, int r) {
for(int i = l; i <= r; i++) {
x[i] = (x[i] - '0' + 1) % 10 + '0';
}
return x;
}
string decr(string x, int l, int r) {
for(int i = l; i <= r; i++) {
x[i] = (x[i] - '0' + 9) % 10 + '0';
}
return x;
}
void BFS() {
q.push({"0000", 0});
for(; q.size(); q.pop()) {
auto tmp = q.front();
if(dis[tmp.first[0] - '0'][tmp.first[1] - '0'][tmp.first[2] - '0'][tmp.first[3] - '0'] != -1) {
continue;
}
dis[tmp.first[0] - '0'][tmp.first[1] - '0'][tmp.first[2] - '0'][tmp.first[3] - '0'] = tmp.second;
for(int i = 0; i < 4; i++) {
for(int j = i; j < 4; j++) {
q.push({inc(tmp.first, i, j), tmp.second + 1});
q.push({decr(tmp.first, i, j), tmp.second + 1});
}
}
}
}
int main() {
freopen("lock.in", "r", stdin);
freopen("lock.out", "w", stdout);
memset(dis, -1, sizeof(dis));
BFS();
for(cin >> t; t; t--) {
cin >> a >> b;
for(int i = 0; i < 4; i++) {
d[i] = (b[i] - a[i] + 10) % 10;
}
cout << dis[d[0]][d[1]][d[2]][d[3]] << '\n';
}
return 0;
}
B diverse
观察第一个字符串的某一个字符的比较过程,发现它与其他字符各会比较一次。
一共这个字符会出现 \(N\) 次,每一次都是一样的,乘以 \(N\) 即可。
\(O(N) - O(|\Sigma|)\)。
code
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, cnt[256], ans;
string s;
signed main() {
freopen("diverse.in", "r", stdin);
freopen("diverse.out", "w", stdout);
cin >> n >> s;
n = s.size();
for(auto i : s) {
cnt[i]++;
}
for(auto i : s) {
ans += n - cnt[i];
}
cout << ans * n << '\n';
return 0;
}
C mono
死因:想到状态,没想到转移。
优化过后的树上背包。
设 \(dp_{x, 0/1}\) 表示只考虑 \(x\) 的子树,作了奇数/偶数次操作的最大黑色节点数,转移自然可以一个一个字数的合并。
注意非叶子节点的 barrier。
\(O(N) - O(N)\)。
code
#include <bits/stdc++.h>
using namespace std;
int n, arr[100010], u, v, dp[100010][2];
vector<int> to[100010];
void DFS(int x, int pa) {
dp[x][1] = -10000000;
for(auto i : to[x]) {
if(i != pa) {
DFS(i, x);
int n0 = max(dp[x][0] + dp[i][0], dp[x][1] + dp[i][1]), n1 = max(dp[x][0] + dp[i][1], dp[x][1] + dp[i][0]);
dp[x][0] = n0, dp[x][1] = n1;
}
}
if(dp[x][1] == -10000000) {
dp[x][1] = 0;
}
dp[x][!arr[x]]++;
//cout << x << ' ' << dp[x][0] << ' ' << dp[x][1] << '\n';
}
int main() {
freopen("mono.in", "r", stdin);
freopen("mono.out", "w", stdout);
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> arr[i];
}
for(int i = 1; i < n; i++) {
cin >> u >> v;
to[u].push_back(v);
to[v].push_back(u);
}
DFS(1, 0);
cout << max(dp[1][0], dp[1][1]) << '\n';
return 0;
}
D weak
死因:取模时会影响相对大小,没有想清楚如何避免取模。
单次操作优先队列 BFS(我认为优先队列 BFS 和 Dijkstra 是两个东西)整出前 \(N - 1\) 小,然后同时减去 \(10^9 + 7\) 的某一个倍数。
注意入队时就要标记。
\(O(N^2 \log N) - O(N)\)。
code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int kMod = (int)(1e9) + 7;
struct node {
int a, b, x;
};
struct cmp {
bool operator ()(const node &a, const node &b) const {
return a.x > b.x;
}
};
int n, arr[100010], brr[100010];
priority_queue<node, vector<node>, cmp> pq;
set<pair<int, int> > st;
void shr(int x) {
arr[x] = arr[x] - arr[1] / kMod * kMod;
}
void shrink() {
pq.push({1, 2, arr[1] + arr[2]});
st.insert({1, 2});
for(int i = 1; i < n; i++) {
node tmp = pq.top();
pq.pop();
brr[i] = tmp.x;
if(tmp.a < n && tmp.a + 1 < tmp.b && !st.count({tmp.a + 1, tmp.b})) {
pq.push({tmp.a + 1, tmp.b, arr[tmp.a + 1] + arr[tmp.b]});
st.insert({tmp.a + 1, tmp.b});
}
if(tmp.b < n && !st.count({tmp.a, tmp.b + 1})) {
pq.push({tmp.a, tmp.b + 1, arr[tmp.a] + arr[tmp.b + 1]});
st.insert({tmp.a, tmp.b + 1});
}
}
for(; pq.size(); pq.pop()) {
}
st.clear();
copy(brr + 1, brr + n, arr + 1);
arr[n] = 0;
n--;
sort(arr + 1, arr + n + 1);
for(int i = 2; i <= n; i++) {
shr(i);
}
shr(1);
}
signed main() {
freopen("weak.in", "r", stdin);
freopen("weak.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> arr[i];
}
for(; n > 1; shrink()) {
}
cout << arr[1] << '\n';
return 0;
}