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)\)
然后大概胡一下只在存在点的高度上建路 就可以离散化
赛时直接对拍验证正确性
但实际上可以证明选没有点的高度上建路一定更劣 因为对于不影响后面一点起始高度的情况下 永远是两个端点才能取最小值
image
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;
}
posted @ 2023-05-28 09:58  Steven24  阅读(26)  评论(0编辑  收藏  举报