SDNU_LqbTraining_2022_1【解题报告】

进入比赛


Problem A. Edge Checker

题意

在一个长度为 10 10 10 的环上,给出两个点 a a a b b b,问点 a a a 和点 b b b 是否相邻.

1 ≤ a ≤ b ≤ 10 1 \leq a \leq b \leq 10 1ab10.

分析

a a a 与点 b b b 相邻等价于点 a a a 是点 b b b 的后继点或者点 b b b 是点 a a a 的后继点.

b % 10 + 1 = a b\%10+1=a b%10+1=a a % 10 + 1 = b a\%10+1=b a%10+1=b.

时间复杂度 O ( 1 ) O(1) O(1),空间复杂度 O ( 1 ) O(1) O(1).

代码
#include <bits/stdc++.h>
using namespace std;

void work() {
	int a, b; scanf("%d %d", &a, &b);
	puts(a % 10 + 1 == b || b % 10 + 1 == a ? "Yes" : "No");
}

int main() {
	work();
	return 0;
}

Problem B. Count Distinct Integers

题意

给出长度为 n n n 的整数序列 a [ 1.. n ] a[1..n] a[1..n],问序列 a a a 中不同元素有多少个.

1 ≤ n ≤ 1 0 3 , 1 ≤ a i ≤ 1 0 9 1 \leq n \leq 10^3, 1 \leq a_i \leq 10^9 1n103,1ai109.

分析

这题怎么做都行,比如:

  1. sort + O(n) 扫一遍判断相邻数是否相同
  2. sort + unique
  3. set

时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn).

PS:unique是什🐎?

代码
#include <bits/stdc++.h>
using namespace std;

const int M = (int)1e3;

int n, a[M + 5];

void work() {
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
	sort(a + 1, a + n + 1);
	printf("%lu\n", unique(a + 1, a + n + 1) - (a + 1));
}

int main() {
	work();
	return 0;
}

Problem C. Jumping Takahashi

题意

在数轴上,小 T 初始站在原点.

接下来走 n n n 步,第 i i i 步小 T 可以选择朝数轴正方向走 a i a_i ai 或者 b i b_i bi.

n n n 步之后,小 T T T 能否走到 x x x.

1 ≤ n , a i , b i ≤ 1 0 2 , 1 ≤ x ≤ 1 0 4 1 \leq n,a_i,b_i \leq 10^2,1 \leq x \leq 10^4 1n,ai,bi102,1x104.

分析

dp 裸体.

f [ i ] [ j ] f[i][j] f[i][j] 表示小 T T T i i i 步之后能否到达 j j j.

则有转移方程 f [ i ] [ j ] = f [ i − 1 ] [ j − a i ] ∣ f [ i − 1 ] [ j − b i ] f[i][j] = f[i-1][j-a_i]|f[i-1][j-b_i] f[i][j]=f[i1][jai]f[i1][jbi].

最后只需要判断 f [ n ] [ x ] f[n][x] f[n][x] 是否为 1 1 1.

时间复杂度 O ( n x ) O(nx) O(nx),空间复杂度 O ( n x ) O(nx) O(nx).

代码
#include <bits/stdc++.h>
using namespace std;

const int M = (int)1e2;
const int N = (int)1e4;

bool f[M + 5][N + 5];

void work() {
	int n, x; scanf("%d %d", &n, &x);
	f[0][0] = 1;
	for(int i = 1, a, b; i <= n; ++i) {
		scanf("%d %d", &a, &b);
		for(int j = 0; j <= x; ++j) if(f[i - 1][j]) {
			if(j + a <= x) f[i][j + a] = 1;
			if(j + b <= x) f[i][j + b] = 1;
		}
	}
	puts(f[n][x] ? "Yes" : "No");
}

int main() {
	work();
	return 0;
}

Problem D. Strange Balls

题意

n n n 个球,第 i i i 个球上标记着一个数字 a i a_i ai.

把这 n n n 个球顺序放入栈中,每当有 a a a 个标着数字 a a a 的球相邻时,这 a a a 个球便会消失.

问每次放球操作后,栈中球的个数.

1 ≤ n ≤ 2 × 1 0 5 , 2 ≤ a i ≤ 2 × 1 0 5 1 \leq n \leq 2 \times 10^5, 2 \leq a_i \leq 2 \times 10^5 1n2×105,2ai2×105.

分析

维护一个栈,存放的是数字 a a a 和数字 a a a 的连续个数.

每次放球只需要更新栈顶即可.

详见代码.

时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n).

代码
#include <bits/stdc++.h>
using namespace std;

stack<pair<int, int>> st;

void work() {
	int n; scanf("%d", &n);
	for(int i = 1, a, ans = 0; i <= n; ++i) {
		scanf("%d", &a);
		++ans;
		if(st.empty()) st.push(make_pair(a, 1));
		else {
			if(st.top().first == a) {
				pair<int, int> p = st.top(); st.pop();
				p.second++;
				if(p.first != p.second) st.push(p);
				else ans -= p.first;
			} else st.push(make_pair(a, 1));
		}
		printf("%d\n", ans);
	}
}

int main() {
	work();
	return 0;
}

Problem E. Ranges on Tree

题意

给一颗 n n n 的点的有根树,根为 1 1 1.

s i s_i si 表示 i i i 号点的子树点集.

要求构造 n n n 个区间 [ l i , r i ] [l_i,r_i] [li,ri],满足:

  1. s i ⊆ s j s_i \subseteq s_j sisj,则 [ l i , r i ] ⊆ [ l j , r j ] [l_i,r_i] \subseteq [l_j,r_j] [li,ri][lj,rj].
  2. s i ⋂ s j = ∅ s_i \bigcap s_j = \emptyset sisj=,则 [ l i , r i ] ⋂ [ l j , r j ] = ∅ [l_i,r_i] \bigcap [l_j,r_j] = \emptyset [li,ri][lj,rj]=.
  3. 最小化 m a x { l 1 , l 2 , ⋯   , l n , r 1 , r 2 , ⋯   , r n } max\{l_1,l_2,\cdots,l_n,r_1,r_2,\cdots,r_n\} max{l1,l2,,ln,r1,r2,,rn}.

2 ≤ n ≤ 2 × 1 0 5 2 \leq n \leq 2 \times 10^5 2n2×105.

分析

手模一下样例可以发现 [ l i , r i ] [l_i,r_i] [li,ri] 选择 s i s_i si 的叶子编号即可.

时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n).

代码
#include <bits/stdc++.h>
using namespace std;

const int M = (int)2e5;
const int inf = 0x3f3f3f3f;

int n;
vector<int> g[M + 5];
int l[M + 5], r[M + 5], cnt;

void dfs(int u, int fa) {
	for(const int& v: g[u]) {
		if(v == fa) continue;
		dfs(v, u);
		l[u] = min(l[u], l[v]);
		r[u] = max(r[u], r[v]);
	}
	if(l[u] == inf) l[u] = r[u] = ++cnt;
}

void work() {
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i) l[i] = inf, r[i] = -inf;
	for(int i = 2, u, v; i <= n; ++i) {
		scanf("%d %d", &u, &v);
		g[u].push_back(v);
		g[v].push_back(u);
	}
	dfs(1, 0);
	for(int i = 1; i <= n; ++i) printf("%d %d\n", l[i], r[i]);
}

int main() {
	work();
	return 0;
}

Problem F. Madoka and Math Dad

题意

T T T 组输入,每组输入给一个整数 n n n.

要求构造一个数 x x x,满足:

  1. x x x 的数位之和为 n n n
  2. x x x 相邻两个数位上的数互异
  3. x x x 最大.

1 ≤ T , n ≤ 1 0 3 1 \leq T,n \leq 10^3 1T,n103.

分析

显然, x x x 的数位上用 1 1 1 2 2 2 间隔填充最优.

手模一下样例可以发现 x x x 的构成与 n % 3 n\%3 n%3 有关,按照规律写一下就完啦.

时间复杂度 O ( T n ) O(Tn) O(Tn),空间复杂度 O ( 1 ) O(1) O(1).

代码
#include <bits/stdc++.h>
using namespace std;

void work() {
	int n; scanf("%d", &n);
	if(n % 3 == 1) printf("1");
	for(int i = 0; i < n / 3; ++i) printf("21");
	if(n % 3 == 2) printf("2");
	printf("\n");
}

int main() {
	int T; scanf("%d", &T);
	while(T--) work();
	return 0;
}

Problem G. Madoka and the Elegant Gift

题意

T T T 组输入,每组输入给一个大小为 n × m n \times m n×m 01 01 01 矩阵.

定义一个子矩阵 A A A 是好的,当且仅当 A A A 只包含 1 1 1 A A A 不被另一个全 1 1 1 矩阵包含.

要求判断是否存在两个不同的好矩阵相交.

1 ≤ T ≤ 2 × 1 0 2 , 1 ≤ n , m ≤ 1 0 2 , ∑ n , ∑ m ≤ 777 1 \leq T \leq 2 \times 10^2, 1 \leq n,m \leq 10^2, \sum{n},\sum{m} \leq 777 1T2×102,1n,m102,n,m777.

分析

画一下图其实可以发现,只需要判断是否存在一下四种情况即可:

在这里插入图片描述

时间复杂度 O ( ∑ n m ) O(\sum{nm}) O(nm),空间复杂度 O ( ∑ n m ) O(\sum{nm}) O(nm).

代码
#include <bits/stdc++.h>
using namespace std;

const int M = (int)1e2;

int n, m;
char s[M + 5][M + 5];

int dx[] = {1, 1, -1, -1};
int dy[] = {1, -1, 1, -1};

void work() {
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; ++i) scanf("%s", s[i] + 1);
	for(int i = 1; i <= n; ++i) {
		for(int j = 1; j <= m; ++j) if(s[i][j] == '1') {
			for(int k = 0; k < 4; ++k) {
				int x = i - dx[k], y = j - dy[k];
				if(x < 0 || x > n || y < 0 || y > m) continue;
				if(s[x][j] == '1' && s[i][y] == '1' && s[x][y] == '0') return printf("NO\n"), void();
			}
		}
	}
	printf("YES\n");
}

int main() {
	int T; scanf("%d", &T);
	while(T--) work();
	return 0;
}

Problem H. Madoka and Childish Pranks

题意

T 组输入,每组输入给一个大小为 n × m n \times m n×m 01 01 01 矩阵 B B B.

初始有一个大小为 n × m n \times m n×m 的全 0 0 0 矩阵 A A A,每次操作可以选一个 A A A 的子矩阵进行棋盘染色.

问若干次操作之后,能否把 A A A 变为 B B B,若能则输出方案.

1 ≤ T ≤ 10 , 1 ≤ n , m ≤ 1 0 2 1 \leq T \leq 10, 1 \leq n,m \leq 10^2 1T10,1n,m102.

分析

通过对 ( x , y − 1 ) (x,y-1) (x,y1) ( x , y ) (x,y) (x,y) 进行棋盘染色,我们可以把矩阵 A A A 的第 2 2 2 列到第 m m m 列染成想要的任意样子.

再考虑第 1 1 1 列,通过对 ( x , y ) (x,y) (x,y) ( x − 1 , y ) (x-1,y) (x1,y) 进行棋盘染色,我们可以把矩阵 A A A 第一列的第 2 2 2 行到第 n n n 行染成想要的任意样子.

因此无解等价于 B [ 1 ] [ 1 ] = 1 B[1][1]=1 B[1][1]=1.

时间复杂度 O ( ∑ n m ) O(\sum{nm}) O(nm),空间复杂度 O ( ∑ n m ) O(\sum{nm}) O(nm).

代码
#include <bits/stdc++.h>
using namespace std;

const int M = (int)1e2;

int n, m;
char s[M + 5][M + 5];
vector<pair<pair<int, int>, pair<int, int>>> ans;

void work() {
	ans.clear();
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; ++i) scanf("%s", s[i] + 1);
	if(s[1][1] == '1') return printf("-1\n"), void();
	for(int i = 1; i <= n; ++i) {
		for(int j = m; j >= 2; --j) if(s[i][j] == '1') {
			ans.push_back(make_pair(make_pair(i, j - 1), make_pair(i, j)));
			s[i][j] = '0';
		}
	}
	for(int i = n; i >= 2; --i) if(s[i][1] == '1') {
		ans.push_back(make_pair(make_pair(i - 1, 1), make_pair(i, 1)));
		s[i][1] = '0';
	}
	int n = (int)ans.size();
	printf("%d\n", n);
	for(int i = 0; i < n; ++i) printf("%d %d %d %d\n", ans[i].first.first, ans[i].first.second, 
												   ans[i].second.first, ans[i].second.second);
}

int main() {
	int T; scanf("%d", &T);
	while(T--) work();
	return 0;
}

Problem I. Madoka and the Best School in Russia

题意

T T T 组输入,每组输入两个整数 x x x d d d.

定义:

  1. 一个数是 good 当且仅当它是 d d d 的倍数
  2. 一个数是 beatiful 当且仅当它是 good 且它不能被表示成两个 good 数之积

现给出 good 数 x x x,问 x x x 能否表示成至少两种不同的方式(若干个 beatiful 数之积).

1 ≤ t ≤ 1 0 2 , 2 ≤ x , d ≤ 1 0 9 1 \leq t \leq 10^2,2 \leq x,d \leq 10^9 1t102,2x,d109.

分析

先来看几个性质

性质一:一个数 a a a 是 good 当且仅当 d ∣ a d \mid a da.

性质二:一个数 a a a 是 beatiful 当且仅当 d ∣ a d\mid a da d 2 ∤ a d^2 \nmid a d2a.

证明:

先证必要性

设两个 good 数 n 1 = k 1 d ,   n 2 = k 2 d n_1=k_1d,\,n_2=k_2d n1=k1d,n2=k2d.

a ≠ n 1 n 2 a\not=n_1n_2 a=n1n2 可得 a ≠ k 1 k 2 d 2 a\not=k_1k_2d^2 a=k1k2d2.

a a a 不可以表示成 k × d 2 k\times d^2 k×d2 的形式.

因此 d 2 ∤ a d^2 \nmid a d2a.

懒得证充分性.

性质三:一个 good 数 x x x 是 beatiful,则 x x x 一定无法表示成两种不同的方式(若干个 beatiful 数之积).

证明:

beatiful 数 x x x 只有一种表示方式,即 x x x.

因此 good 数 x x x 能够表示成两种不同的方式(若干个 beatiful 数之积),必须要满足 d 2 ∣ x d^2 \mid x d2x.

分解 x x x 得到 x = k d × d c    ( k , d ) = 1 x=kd \times d^c \,\, (k,d)=1 x=kd×dc(k,d)=1.

显然有第一种方案: x = k d × d × ⋯ × d ⏟ c 个 d x=kd \times \underbrace{d \times \cdots \times d}_{c个d} x=kd×cd d××d.

另外的方案则是去考虑 k k k 能否拆分或者最后一个 d d d 能否拆分.

时间复杂度 O ( ∑ d ) O(\sum{\sqrt{d}}) O(d ),空间复杂度 O ( 1 ) O(1) O(1).

代码
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

bool is_prime(int n) {
	if(n == 1) return 0;
	for(int i = 2; (ll)i * i <= n; ++i) if(n % i == 0) return 0;
	return 1;
}

void work() {
	ll x, d; scanf("%lld %lld", &x, &d);
	if(x % (d * d)) return printf("NO\n"), void();
	ll k = x / d, c = 0;
	while(k % d == 0) k /= d, ++c;
	if((c >= 3 && !is_prime(d)) || (k > 1 && !is_prime(k))) return printf("YES\n"), void();
	if(c == 1 || is_prime(d)) return printf("NO\n"), void();
	for(int i = 2; (ll)i * i <= d; ++i) if(d % i == 0) {
		if(k * i % d || k * d / i % d) return printf("YES\n"), void();
	}
	printf("NO\n");
}

int main() {
	int T; scanf("%d", &T);
	while(T--) work();
	return 0;
}

Problem J. Nearest Excluded Points

题意

二维平面上有 n n n 个互异整数点 ( x i , y i ) (x_i,y_i) (xi,yi),对每个点求出一个曼哈顿距离最近点 ( x , y ) (x,y) (x,y),满足 ( x , y ) (x,y) (x,y) 不在那 n n n 个点当中.

1 ≤ n , x i , y i ≤ 2 × 1 0 5 1 \leq n,x_i,y_i \leq 2 \times 10^5 1n,xi,yi2×105.

分析

n n n 个点的每个点向上下左右连边,做一个超级源反向 bfs 即可.

代码
#include <bits/stdc++.h>
using namespace std;

const int M = (int)2e5;

int n, m;
int x[M + 5], y[M + 5];
int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, 1, -1};
set<pair<int, int>> vis;
set<pair<int, int>> st[2];
map<pair<int, int>, pair<int, int>> res;

void work() {
	scanf("%d", &n); m = 0;
	for(int i = 1; i <= n; ++i) {
		scanf("%d %d", &x[i], &y[i]);
		st[0].insert(make_pair(x[i], y[i]));
		for(int j = 0; j < 4; ++j) st[0].insert(make_pair(x[i] + dx[j], y[i] + dy[j])), 
								   st[1].insert(make_pair(x[i] + dx[j], y[i] + dy[j]));
	}
	for(int i = 1; i <= n; ++i) st[1].erase(make_pair(x[i], y[i]));
	queue<pair<int, int>> q;
	for(set<pair<int, int>>::iterator i = st[1].begin(); i != st[1].end(); ++i) {
		q.push(*i);
		vis.insert(*i);
		res[*i] = *i;
	}
	while(!q.empty()) {
		pair<int, int> u = q.front(); q.pop();
		for(int i = 0; i < 4; ++i) {
			pair<int, int> v = u;
			v.first += dx[i], v.second += dy[i];
			if(st[0].count(v) && !res.count(v)) {
				res[v] = res[u];
				q.push(v);
			}
		}
	}
	for(int i = 1; i <= n; ++i) {
		pair<int, int> p = res[make_pair(x[i], y[i])];
		printf("%d %d\n", p.first, p.second);
	}
}

int main() {
	work();
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值