小学生第二次模拟赛题解

好吃的序列

这是一道送分题。
在 stl 库里面有个 nth_element 的函数,用来求序列中的第 \(k\) 小。

#include<bits/stdc++.h>
using namespace std;
int a[N];
int32_t main(){
	int n, k, seed;
	read(n, k, seed);
	mt19937 myrd(seed);
	FL(i, 1, n)a[i] = myrd();
	nth_element(a + 1, a + k, a + 1 + n);
	put(a[k], endl);
	return 0;
}

这个 nth_element 函数是这么实现的:

  • 选一个哨兵。
  • 使哨兵左边的数都小于他,右边的数都大于他,实现同快排。
  • 如果哨兵的位置 \(> k\) 那么查询他的左区间,如果 \(< k\) 查询有区间,否则返回这个哨兵。
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define FL(a, b, c) for(int a = (b), a##end = (c); a <= a##end; a++)
#define FR(a, b, c) for(int a = (b), a##end = (c); a >= a##end; a--)
constexpr int N = 1e8 + 10;
int a[N];
mt19937 myrd(chrono::steady_clock::now().time_since_epoch().count());
#define rd(l, r) uniform_int_distribution<int>(l, r)(myrd)
int find(int l, int r, int k){
	while(r - l > 2){
		int i = l, j = r, mid = rd(l, r);//随机选取哨兵
		swap(a[l], a[mid]), mid = a[i];
		while(i < j){
			while(i < j && a[j] >= mid)j--;
			while(i < j && a[i] <= mid)i++;
			swap(a[i], a[j]);
		}
		swap(a[i], a[l]);
		if(i == k)return a[i];
		(i < k) ? (l = i + 1) : r = i - 1;
	}
    //数量小于 3 时插排
	int w, j;
	FL(i, l + 1, r)
		if((w = a[i]) < a[i - 1]){
			for(j = i - 1; j >= l && w < a[j]; j--)
				a[j + 1] = a[j];
			a[j + 1] = w;
		}
	return a[k];
}
int32_t main(){
	cin.tie(0)->sync_with_stdio(0);
	int n, k, seed;
	cin >> n >> k >> seed;
	mt19937 rand(seed);
	FL(i, 1, n)a[i] = rand();
	cout << find(1, n, k);
	return 0;
}

好吃的串串

我们先把 \(f(1)\) 单独拆开:

void f(int x, int y = 0){for(a[y = x] ^= 1; (y += x) <= n;)f(y);}
for(int i = 1; i <= n; i++)a[i] = 0;
a[1] = 1;
for(int y = 1; (++y) <= n;)f(y)
for(int i = 2; i <= n; i++)f(i);

我们注意到后面两句话是相等的,且异或两次就会变成 \(1\)
所以,如果 \(k\)\(1\),输出 yes 否则输出 No

#include<bits/stdc++.h>
using namespace std;
int main(){
    int t, n, k;
    cin >> t;
    while(t--)
		cin >> n >> k, cout << ((k ^ 1) ? "No": "Yes")  << endl;
}
[折叠代码]

好吃的蛋糕

原题:大河的序列
容易发现最大美味度为最大值的 \(2\) 倍。

证明

我现在的区间只有一个最大值 \(a\),现在要加入一个 \(b\)
如果 \(b = a\) 那么答案不变。
否则,必然有一位不同,如果答案要变大那么这一位 \(a\)\(0\),且 \(b\)\(1\),那么 \(b > a\),但 \(a\) 是最大值。
如果 \(a\)\(1\)\(b\)\(0\),那么答案必然变小。

接下来考虑维护最大值。
一种是直接用线段树维护,单点修改,区间查询。

我们也可以维护一个当前的数组,和一个堆。
堆内是几对二元组 \((x, y)\),并以 \(x\) 升序排序,其中 \(x\) 表示美味度,\(y\) 表示最大值的位置。
每次修改后,在堆里插入修改的值和修改的位置
每次拿出堆顶,如果 \(a[y] \ne x\),说明 \(y\) 已经修改过了,直接弹掉,否则输出。
最后。

点击展开代码
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define FL(a, b, c) for(int a = (b), a##end = (c); a <= a##end; a++)
#define FR(a, b, c) for(int a = (b), a##end = (c); a >= a##end; a--)
#define lowbit(x) ((x) & -(x))
#define eb emplace_back
#define int long long
constexpr int N = 1e6 + 10;
int a[N];
priority_queue<pair<int, int>>ma;
int32_t main(){
	cin.tie(0)->sync_with_stdio(0);
	int n, q;
	cin >> n >> q;
	FL(i, 1, n)cin >> a[i], ma.emplace(a[i], i);
	while(q--){
		int k, x;
		cin >> x >> k;
		a[x] = k, ma.emplace(k, x);
		while(a[ma.top().second] != ma.top().first)ma.pop();
		cout << ma.top().first * 2 << endl; 
	}
	return 0;
}

好吃的方阵

暴力是显然的。

暴力
#include<bits/stdc++.h>
#define endl '\n'
#define FL(a, b, c) for(int a = (b), a##end = (c); a <= a##end; a++)
#define FR(a, b, c) for(int a = (b), a##end = (c); a >= a##end; a--)
#define lowbit(x) ((x) & -(x))
#define eb emplace_back
using namespace std;
int a[810][810];
int main(){
	int n,q;
    cin.tie(0)->sync_with_stdio(0);
	cin >> n >> q;
	FL(i, 1, n)FL(j, 1, n)cin >> a[i][j];
	int x, i, j, k;
	while(q--)
		switch(cin >> x, x){
			case 1:cin >> i >> j >> k, a[i][j] = k;break;
			case 2:
				cin >> i >> j;
				FL(l, 1, n)swap(a[i][l], a[j][l]);break;
			case 3:
				cin >> i >> j;
				FL(l, 1, n)swap(a[l][i], a[l][j]);break;
			case 4:
				FL(i, 1, n){
                    FL(j, 1, n)cout << a[i][j] << " ";
					cout << endl;
				}
                cout << endl;break;
		}
	return 0;
}

暴力慢,是慢在了 \(2\)\(3\) 的部分。
我们记录 \(b\)\(c\) 两个数组,表示现在的行列对应原本的行列。
这样除了 \(4\) 操作,其他的都是 \(O(1)\) 了。

#include<bits/stdc++.h>
#define endl '\n'
#define FL(a, b, c) for(int a = (b), a##end = (c); a <= a##end; a++)
#define FR(a, b, c) for(int a = (b), a##end = (c); a >= a##end; a--)
#define lowbit(x) ((x) & -(x))
#define eb emplace_back
using namespace std;
int a[810][810], b[810], c[810];
int main(){
	int n,q;
    cin.tie(0)->sync_with_stdio(0);
	cin >> n >> q;
	FL(i, 1, n)FL(j, 1, n)cin >> a[i][j];
    FL(i, 1, n)b[i] = i, c[i] = i;
	int x, i, j, k;
	while(q--)
		switch(cin >> x, x){
			case 1:cin >> i >> j >> k, a[b[i]][c[j]] = k;break;
			case 2:cin >> i >> j;swap(b[i], b[j]);break;
			case 3:cin >> i >> j;swap(c[i], c[j]);break;
			case 4:
				FL(i, 1, n){
                    FL(j, 1, n)cout << a[b[i]][c[j]] << " ";
					cout << endl;
				}
                cout << endl;break;
		}
	return 0;
}

好吃的炸弹

先二分承受度,再 BFS 就好了。
不过直接 BFS 重复的有点多,会 TLE。
我们把队列换成双端队列,来模拟优先队列,把不需要炸弹的放在头部,需要的放在尾部,每次取头部即可。

点击展开代码
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define FL(a, b, c) for(int a = (b), a##end = (c); a <= a##end; a++)
#define FR(a, b, c) for(int a = (b), a##end = (c); a >= a##end; a--)
#define lowbit(x) ((x) & -(x))
#define eb emplace_back
constexpr int N = 1010;
#define getchar()(p1==p2&&(p2=(p1=in)+fread(in,1,maxn,stdin),p1==p2)?EOF:*p1++)
#define flush()(fwrite(out,1,p3-out,stdout))
#define putchar(x)(p3==out+maxn&&(flush(),p3=out),*p3++=(x))
constexpr auto maxn=1<<20;char in[maxn],out[maxn],*p1=in,*p2=in,*p3=out;class Flush{public:~Flush(){flush();}}_;
void read(int&x){x=0;char ch=getchar();while(!isdigit(ch))ch=getchar();while(isdigit(ch))x=(x<<1)+(x<<3)+(ch^48),ch=getchar();}void write(int x){static short Stack[50],top(0);do Stack[++top]=x%10,x/=10;while(x);while(top)putchar(Stack[top--]|48);}
int a[N][N], dx[] = {0, 1, -1, 0}, dy[] = {1, 0, 0, -1}, vis[N][N], tot, k, n;
bool bfs(int z){
	deque<pair<int, int>>q;
	q.emplace_front(1, 1);
	memset(vis, 0x5f, sizeof vis);
	vis[1][1] = a[1][1] > z;
	while(!q.empty()){
		int x = q.front().first, y = q.front().second;
		q.pop_front();
		FL(i, 0, 3){
			int x1 = x + dx[i], y1 = y + dy[i];
			if(x1 < 1 || y1 < 1 || x1 > n || y1 > n)continue;
			int w = a[x1][y1] > z;
			if(w + vis[x][y] < vis[x1][y1]){
				vis[x1][y1] = w + vis[x][y];
				if(!w)q.emplace_front(x1, y1);
				else q.emplace_back(x1, y1);
			}

		}
	}
	return vis[n][n] <= k;
}
int32_t main(){
	int r = 0, l;
	read(n), read(k);
	FL(i, 1, n)FL(j, 1, n)read(a[i][j]), r = max(r, a[i][j]);
	while(l < r){
		int mid = l + r >> 1;
		if(bfs(mid))r = mid;
		else l = mid + 1;
	}
	write(l);
	return 0;
}

好吃的烧饼

\[\begin{aligned} ans &= \sum_{i=x_1}^{x_2}\sum_{j=y_1}^{y_2}M_{i, j} \times [(i - x_1)(y_2-y_1+1)+(j-y_1+1)] \\ &= \sum_{i=x_1}^{x_2}\sum_{j=y_1}^{y_2}M_{i, j} \times [i(y_2-y_1+1)-x_1(y_2-y_1+1)-(y_1-1)+j] \\ &= [-x_1(y_2-y_1+1)-y_1+1] \times \sum_{i = x_1}^{x_2}\sum_{j = y_1}^{y_2}M_{i, j} + (y_2-y_1+1)\sum_{i = x_1}^{x_2}\sum_{j = y_1}^{y_2}iM_{i, j} + \sum_{i = x_1}^{x_2}\sum_{j = y_1}^{y_2}jM_{i, j}. \end{aligned} \]

用二维前缀和维护 \(M_{i,j}​, iM_{i,j},jM_{i,j}\) 即可。

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define FL(a, b, c) for(int a = (b), a##end = (c); a <= a##end; a++)
#define FR(a, b, c) for(int a = (b), a##end = (c); a >= a##end; a--)
#define lowbit(x) ((x) & -(x))
#define eb emplace_back
constexpr int N = 2100;
#define int long long
int a[N][N], b[N][N], c[N][N];
#define sum(a) (a[x2][y2] - a[x1 - 1][y2] - a[x2][y1 - 1] + a[x1 - 1][y1 - 1])
int32_t main(){
	cin.tie(0)->sync_with_stdio(0);
	int n, m;
	cin >> n >> m;
	FL(i, 1, n)FL(j, 1, n)cin >> a[i][j], b[i][j] = a[i][j] * i, c[i][j] = a[i][j] * j;
	FL(i, 1, n)FL(j, 2, n)a[i][j] += a[i][j - 1], c[i][j] += c[i][j - 1], b[i][j] += b[i][j - 1];
	FL(i, 2, n)FL(j, 1, n)a[i][j] += a[i - 1][j], c[i][j] += c[i - 1][j], b[i][j] += b[i - 1][j];
	while(m--){
		int x1, x2, y1, y2;
		cin >> x1 >> y1 >> x2 >> y2;
		cout << sum(a) * (x1 * (y1 - y2 - 1) - y1 + 1) + sum(b) * (y2 - y1 + 1) + sum(c) << endl;
	}
	return 0;
}
posted @ 2025-02-16 17:48  fush's_blog  阅读(81)  评论(0)    收藏  举报