20230525 solution ( catchcow + grading + magic )
T1
题目传送门:P1588 [USACO07OPEN]Catch That Cow S
广搜 + vis 数组就能过 复杂度 O(n)
code:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 0721;
int stp[N];
int q[N];
bool vis[N];
int n, s, e;
void bfs(int x) {
vis[x] = 1;
int h = 1, t = 0;
q[++t] = x;
stp[x] = 0;
while (h <= t) {
int now = q[h];
++h;
if (now - 1 >= 0 && vis[now - 1] == 0) {
q[++t] = now - 1;
stp[now - 1] = stp[now] + 1;
vis[now - 1] = 1;
}
if (now + 1 <= 1e6 && vis[now + 1] == 0) {
q[++t] = now + 1;
stp[now + 1] = stp[now] + 1;
vis[now + 1] = 1;
}
if ((now * 2) <= 1e6 && vis[now * 2] == 0) {
q[++t] = (now * 2);
stp[now * 2] = stp[now] + 1;
vis[now * 2] = 1;
}
}
}
int main() {
scanf("%d", &n);
while (n--) {
memset(vis, 0, sizeof vis );
scanf("%d%d", &s, &e);
bfs(s);
printf("%d\n",stp[e]);
}
return 0;
}
T2
题目传送门:P2893 [USACO08FEB] Making the Grade G
首先能看出来这是个 dp
然后可以很快写出一个以路段编号为第一维 高度为第二维的二维 dp
但是显然空间时间都过不去 考虑优化
看到 n <= 2000 猜想可能是 \(O(n^2)\)
然后大概胡一下只在存在点的高度上建路 就可以离散化
赛时直接对拍验证正确性
但实际上可以证明选没有点的高度上建路一定更劣 因为对于不影响后面一点起始高度的情况下 永远是两个端点才能取最小值
code:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 0x0d00;
ll f[N][N];
int now[N], val[N];
int n, maxn;
ll ans = 0x7ffffffffffffff;
void lsh() {
memcpy(now, val, sizeof val );
sort(val + 1, val + 1 + n);
int k = unique(val + 1, val + 1 + n) - val - 1;
for (int i = 1; i <= n; ++i) {
now[i] = lower_bound(val + 1, val + 1 + k, now[i]) - val;
maxn = max(now[i], maxn);
}
}
void dp1() {
for (int i = 1; i <= n; ++i) {
for (int j = 0; j <= maxn; ++j) {
if (j == 0) f[i][j] = f[i - 1][j] + abs(val[j] - val[now[i]]);
else f[i][j] = min(f[i - 1][j], f[i][j - 1] - abs(val[j - 1] - val[now[i]])) + abs(val[j] - val[now[i]]);
}
}
for (int i = 0; i <= maxn; ++i) ans = min(ans, f[n][i]);
}
void dp2() {
for (int i = 1; i <= n; ++i) {
for (int j = maxn; j >= 0; --j) {
if (j == maxn) f[i][j] = f[i - 1][j] + abs(val[j] - val[now[i]]);
else f[i][j] = min(f[i - 1][j], f[i][j + 1] - abs(val[j + 1] - val[now[i]])) + abs(val[j] - val[now[i]]);
}
}
for (int i = 0; i <= maxn; ++i) ans = min(ans, f[n][i]);
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &val[i]);
lsh();
dp1();
dp2();
printf("%lld",ans);
return 0;
}
T3
题目传送门:P2801 教主的魔法
首先想到线段树 但对于查询操作线段树似乎不可做(虽然这题好像有一个假的的线段树做法能过)
n <= 1e6 但询问次数只有 3000 发现分块可做
瓶颈在于查询时如何查询整块的信息(赛时就被这个卡住了)
发现对于一个块内 元素的顺序是无所谓的
所以可以预处理的时候排序然后二分查找
相应的 单点的修改操作也要修改之后对于当前块重新排序
设块长为 B
预处理复杂度 \(O(\frac{n}{B} * BlogB)\)
修改复杂度 \(O(BlogB + \frac{n}{B})\)
查询复杂度 \(O(B + \frac{n}{B} * logB)\)
code:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e6 + 0721;
const int M = 1e3 + 0721;
ll a[N], mem[N];
int loc[N];
struct node {
int l, r;
ll sum;
} block[M];
int n, q, cnt;
void init() {
memcpy(mem, a, sizeof a );
const int len = sqrt(n);
int x = 1;
while (x <= n) {
block[++cnt].l = x;
x += len;
x = min(x, n + 1);
block[cnt].r = x - 1;
}
for (int i = 1; i <= cnt; ++i) {
for (int j = block[i].l; j <= block[i].r; ++j) loc[j] = i;
sort(mem + block[i].l, mem + 1 + block[i].r);
}
}
void modify(int l, int r, int x) {
int s = loc[l], e = loc[r];
if (l > block[s].l) {
for (int i = l; i <= block[s].r; ++i) a[i] += x;
for (int i = block[s].l; i <= block[s].r; ++i) mem[i] = a[i];
sort(mem + block[s].l, mem + 1 + block[s].r);
++s;
}
if (r < block[e].r) {
for (int i = block[e].l; i <= r; ++i) a[i] += x;
for (int i = block[e].l; i <= block[e].r; ++i) mem[i] = a[i];
sort(mem + block[e].l, mem + 1 + block[e].r);
--e;
}
for (int i = s; i <= e; ++i) block[i].sum += x;
}
void query(int l, int r, int x) {
int s = loc[l], e = loc[r];
int ans = 0;
if (l > block[s].l) {
for (int i = l; i <= block[s].r; ++i) {
if (a[i] + block[loc[i]].sum >= x) ++ans;
}
++s;
}
if (r < block[e].r) {
for (int i = block[e].l; i <= r; ++i) {
if (a[i] + block[loc[i]].sum >= x) ++ans;
}
--e;
}
for (int i = s; i <= e; ++i) {
int line = lower_bound(mem + block[i].l, mem + 1 + block[i].r, x - block[i].sum) - mem;
ans += block[i].r - line + 1;
}
printf("%d\n",ans);
}
int main() {
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
init();
while (q--) {
char opt;
int l, r, x;
cin >> opt >> l >> r >> x;
// scanf("%c%d%d%d", &opt, &l, &r, &x);
if (opt == 'M') modify(l, r, x);
else query(l, r, x);
}
return 0;
}