7_22 模拟赛
T1
解题思路
这道题手玩样例后就有了大致的感觉,如果倒推一定是 \(0\) 内部自行解决自己合并,而其他的数都减一,正确性可以稍微感性理解一下,首先往下减少一定是希望一次性减得越多越好,因为总和一定,最后目标和为 \(0\),所以有其他的数都大力减少的想法,而如果有一些数变成了 \(0\),我们其实并不急着合并,因为让别的数减少,出现新的 \(0\) 再去合并结果起码不会更劣,只是改了合并时间而已,故考虑有 \(0\) 内部解决,其他数全部变小。
Code:
#include <algorithm>
#include <cstdio>
using namespace std;
const int N = 1000010;
int n;
int cnt[N];
int ans;
int main() {
scanf("%d", &n);
for (int i = 1, tmp; i <= n; i++) {
scanf("%d", &tmp);
cnt[tmp]++;
}
int ct = 0;
int tmpnum = cnt[ct];
while (n > 1) {
ans++;
n -= tmpnum >> 1;
tmpnum = (tmpnum + 1) >> 1;
tmpnum += cnt[++ct];
}
printf("%d\n", ans);
return 0;
}
因为自己没有写所以贺了 \(\rm amr2020\) 神仙的代码。
T2
解题思路:
注意到天数达到 \(1000\) 时代价很高,此时代价不小于所得,所以可以暴力枚举天数来做,设 f[t][i]
表示第 \(t\) 天恰到 \(j\) 的最长路,求答案时暴力枚举第几天更新 ans
即可。
Code:
#include<bits/stdc++.h>
using namespace std;
const int N=1008,M=2008,T=1005;
int n,m,c;
int moy[N];
int h[N],cnt=0;
struct edg{
int to,nxt;
}e[M];
int f[N][T],ans;
void add(int u,int v){
e[++cnt]=(edg){v,h[u]};
h[u]=cnt;
}
int main(){
memset(f,-1,sizeof(f));
scanf("%d%d%d",&n,&m,&c);
for(int i=1;i<=n;i++){
scanf("%d",&moy[i]);
}
for(int x,y,i=1;i<=m;i++){
scanf("%d%d",&x,&y);
add(x,y);
}
f[1][0]=0;
for(int t=1;t<=1000;t++){
for(int i=1;i<=n;i++){
if(f[i][t-1]==-1)continue;
for(int p=h[i];p;p=e[p].nxt){
int to=e[p].to;
f[to][t]=max(f[i][t-1]+moy[to],f[to][t]);
}
}
}
for(int i=1;i<=1000;i++){
ans=max(ans,f[1][i]-c*i*i);
}
printf("%d\n",ans);
return 0;
}
T3
解题思路:
比较平凡的思路,设 \(s[r][l]\) 表示强制右端点为 \(r\) 左端点在 \(l \to r\) 范围内的答案和,那么求答案暴力枚举就行了,\(\cal O \rm(qn)\) 可以通过本题,更好的,离线后珂以做到 \(\cal O \rm(n^2)\),珂以通过。
Code:
#include <bits/stdc++.h>
using i64 = long long;
constexpr int inf = 1e9;
struct Ques {
int l, r, id;
};
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, q;
std::cin >> n >> q;
int mx = -inf, mi = inf;
std::vector<int> a(n + 1);
for (int i = 1; i <= n; ++i)
std::cin >> a[i], mx = std::max(mx, a[i]), mi = std::min(mi, a[i]);
for (int i = 1; i <= n; ++i)
a[i] -= mi;
std::vector<std::vector<i64>> s(n + 1, std::vector<i64>(n + 1, 0));
std::vector<int> buc(mx - mi + 1);
for (int r = 3; r <= n; ++r) {
i64 now = 0;
for (int l = r - 1; l >= 1; --l) {
if (3 * mi + a[l] + a[r] <= 0 && 3 * mi + a[l] + a[r] >= -mx + mi) {
now += buc[-(3 * mi + a[l] + a[r])];
}
s[r][l] = now;
++buc[a[l]];
}
for (int l = r - 1; l >= 1; --l)
--buc[a[l]];
}
std::vector<Ques> qs(q + 1);
for (int i = 1; i <= q; ++i) {
int a, b;
std::cin >> a >> b;
qs[i] = {a, b, i};
}
std::sort(qs.begin() + 1, qs.begin() + q + 1, [](Ques a, Ques b) {
if (a.l != b.l)
return a.l < b.l;
return a.r < b.r;
});
std::vector<i64> ans(q + 1);
for (int i = 1; i <= q;) {
int j = i;
while (j <= q && qs[i].l == qs[j].l)
++j;
int r = qs[i].l + 1;
i64 res = 0;
for (int k = i; k < j; ++k) {
while (r < qs[k].r) {
++r;
res += s[r][qs[i].l];
}
ans[qs[k].id] = res;
}
i = j;
}
for (int i = 1; i <= q; ++i)
std::cout << ans[i] << '\n';
}
T4
解题思路:
二维偏序限制下的的转移,将跳板拆为起点和终点后可以用树状数组来做,也可以 cdq 分治处理。
Code:
#include <bits/stdc++.h>
struct Bit {
int n;
std::vector<int> c;
Bit(int n) : c(n + 1, 0), n(n) {}
inline void add(int p, int v) {
while (p <= n)
c[p] = std::max(c[p], v), p += p & -p;
}
inline int qry(int p) {
int res = 0;
while (p)
res = std::max(res, c[p]), p -= p & -p;
return res;
}
};
signed main() {
// freopen("1.in", "r", stdin);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, p;
std::cin >> n >> p;
int sum = n + n;
++n;
struct Point { int x, y, ty, id; };
std::vector<int> disc;
std::vector<Point> a;
a.push_back({1, 1, 1, 0});
disc.push_back(1);
std::vector<int> len(p + 1);
for (int i = 1; i <= p; ++i) {
int x1, y1, x2, y2;
std::cin >> x1 >> y1 >> x2 >> y2;
++x1, ++y1, ++x2, ++y2;
len[i] = x2 - x1 + y2 - y1;
a.push_back({x1, y1, 0, i});
a.push_back({x2, y2, 1, i});
disc.push_back(y1), disc.push_back(y2);
}
a.push_back({n, n, 2, p + 1});
disc.push_back(n);
std::sort(disc.begin(), disc.end());
int l = std::unique(disc.begin(), disc.end()) - disc.begin();
for (auto &it : a) {
int val = it.y;
it.y = std::lower_bound(disc.begin(), disc.begin() + l, val) - disc.begin() + 1;
}
std::sort(a.begin(), a.end(), [&](Point a, Point b) {
if (a.x != b.x) return a.x < b.x;
if (a.y != b.y) return a.y < b.y;
return a.id < b.id;
});
std::vector<int> f(p + 2, 0);
Bit b(l);
for (auto it : a) {
if (!it.ty) {
assert(it.y <= l);
int res = b.qry(it.y);
f[it.id] = std::max(f[it.id], res + len[it.id]);
}
else if (it.ty == 1) {
assert(it.y >= 1);
b.add(it.y, f[it.id]);
}
else if (it.ty == 2) {
int res = b.qry(it.y);
std::cout << sum - res << '\n';
exit(0);
}
}
}
T5
解题思路:
由于区间取反难以处理考虑差分,差分后的结果数组是容易求出的,而修改可以理解为平移,那么处理出每个点平移到别的地方的代价,状压 dp 即可。
Code:
// Problem: #520. 密码
// Contest: UOJ
// URL: https://sjzezoj.com/problem/520
// Memory Limit: 1024 MB
// Time Limit: 1000 ms
// Author: Railgun
// Start Time: 2021-07-22 17:05:28
#include <bits/stdc++.h>
constexpr int inf = 2e9;
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, k, m;
std::cin >> n >> k >> m;
++n;
std::vector<int> vis(n);
for (int i = 0; i < k; ++i) {
int w;
std::cin >> w;
--w;
vis[w] ^= 1, vis[w + 1] ^= 1;
}
std::vector<int> stk;
for (int i = 0; i < n; ++i) {
if (vis[i])
stk.emplace_back(i);
}
std::vector<int> len(m);
for (int i = 0; i < m; ++i)
std::cin >> len[i];
std::vector<std::vector<int>> dist(stk.size(), std::vector<int>(stk.size(), inf));
auto bfs = [&](int s) {
std::vector<int> dis(n, inf);
dis[stk[s]] = 0;
std::queue<int> q;
q.push(stk[s]);
while (q.size()) {
int x = q.front();
q.pop();
for (auto it : len) {
if (x - it >= 0 && dis[x - it] > dis[x] + 1)
dis[x - it] = dis[x] + 1, q.push(x - it);
if (x + it < n && dis[x + it] > dis[x] + 1)
dis[x + it] = dis[x] + 1, q.push(x + it);
}
}
for (int i = 0; i < stk.size(); ++i)
dist[s][i] = dis[stk[i]];
};
for (int i = 0; i < stk.size(); ++i)
bfs(i);
std::vector<int> dp(1 << stk.size(), inf);
dp[0] = 0;
for (int s = 0; s < (1 << stk.size()); ++s) {
if (dp[s] == inf)
continue;
for (int i = 0; i < stk.size(); ++i) {
if (!((s >> i) & 1)) {
for (int j = i + 1; j < stk.size(); ++j) {
if (!((s >> j) & 1)) {
int t = s ^ (1 << i) ^ (1 << j);
dp[t] = std::min(dp[t], dp[s] + dist[i][j]);
}
}
}
}
}
if (dp[(1 << stk.size()) - 1] == inf) std::cout << "-1" << '\n';
else std::cout << dp[(1 << stk.size()) - 1] << '\n';
}
我不想就这样沦陷,迷失在黑夜,我将燃烧这生命,就算再壮烈。