2022蓝桥杯题解

2022 蓝桥杯

C 求和

题意:

给定数组 a a a ,求 ∑ i = 1 n ∑ j = i + 1 n a i a j \sum\limits_{i=1}\limits^n\sum\limits_{j=i+1}\limits^n a_ia_j i=1nj=i+1naiaj

解析:

∑ i = 1 n ∑ j = i + 1 n a i a j = ∑ i = 1 n a i ( ∑ j = i + 1 n a j ) = ∑ i = 1 n a i ( s u m [ n ] − s u m [ i ) \sum\limits_{i=1}\limits^n\sum\limits_{j=i+1}\limits^n a_ia_j = \sum\limits_{i=1}\limits^na_i(\sum\limits_{j=i+1}\limits^n a_j) = \sum\limits_{i=1}\limits^na_i(sum[n]-sum[i) i=1nj=i+1naiaj=i=1nai(j=i+1naj)=i=1nai(sum[n]sum[i)
s u m sum sum a a a 数组的前缀和,预处理前缀和即可

代码:

#include
using namespace std;
#define fi first
#define se second
const int maxn = 2e5+10;
const int INF = 0x3f3f3f3f;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;

int n;
ll a[maxn], sum[maxn]; 
ll ans;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n;
	for(int i = 1; i <= n; i++){
		cin >> a[i];
		sum[i] = sum[i-1] + a[i];
	}
	
	for(int i = 1; i <= n; i++){
		ans += a[i]*(sum[n]-sum[i]);
	}
	cout << ans << endl;
	return 0;
}


D 选数异或

题意:

给定数组 a a a 和整数 x x x m m m 次询问。每次询问 a [ l . . . r ] a[l...r] a[l...r] 中是否存在两个数异或值为 x x x

解析:

对于每个 a i a_i ai,找到左边最近的 a j a_j aj ,满足 a i ⊕ a j = x a_i \oplus a_j = x aiaj=x,即 f [ i ] = j f[i] = j f[i]=j 。然后线段树维护 f f f 的最大值

对于每次询问 ( l , r ) (l,r) (l,r) ,判断 f [ i ] f[i] f[i] 是否大于等于 l l l

代码:

#include
using namespace std;
#define fi first
#define se second
const int maxn = 1e6+10;
const int INF = 0x3f3f3f3f;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;

int n, m, x;
int pos[1<<21], a[maxn], f[maxn];
int t[maxn<<2];
inline int ls(int x){return x << 1;}
inline int rs(int x){return x << 1 | 1;}
void pushup(int k){
	t[k] = max(t[ls(k)], t[rs(k)]);
}
void build(int k, int l, int r){
	if(l == r){
		t[k] = f[l];
		return;
	}
	int mid = (l+r) >> 1;
	build(ls(k), l, mid);
	build(rs(k), mid+1, r);
	pushup(k);
}
int query(int k, int l, int r, int x, int y){
	if(x <= l && y >= r)
		return t[k];
	int mid = (l+r) >> 1;
	int res = 0;
	if(x <= mid)
		res = max(res, query(ls(k), l, mid, x, y));
	if(y > mid)
		res = max(res, query(rs(k), mid+1, r, x, y));
	return res; 
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n >> m >> x;
	for(int i = 1; i <= n; i++){
		cin >> a[i];
		f[i] = pos[a[i]^x];
		pos[a[i]] = i;
		
	}
	build(1, 1, n);
	for(int i = 1; i <= m; i++){
		int l, r;
		cin >> l >> r;
		if(query(1, 1, n, l, r) >= l)
			cout << "yes" << endl;
		else
			cout << "no" << endl;
 	}
	return 0;
}


E 爬树的甲壳虫

题意:

高度为 n n n 的树,初始位于高度为 0 0 0 处。当从高度 i − 1 i-1 i1 爬到 i i i 处时,有 p i p_i pi 的概率掉回高度为 0 0 0 处。询问爬到高度为 n n n 处的时间的期望值

解析:

f i f_i fi 为从高度 i i i 爬到树顶的期望时间,则 f i − 1 = p i f 0 + ( 1 − p i ) f i + 1 f_{i-1} = p_if_0+(1-p_i)f_i+1 fi1=pif0+(1pi)fi+1 f n = 0 f_n = 0 fn=0,答案为 f 0 f_0 f0

因为 f n = 0 f_n = 0 fn=0 ,可以用 f 0 f_0 f0 和常数表示 f i f_i fi ,即 f i = a i f 0 + b i f_i = a_if_0+b_i fi=aif0+bi代入上式可得: f i − 1 = [ p i + a i ( 1 − p i ) ] f 0 + [ ( 1 − p i ) b i + 1 ] f_{i-1} = [p_i+a_i(1-p_i)]f_0+[(1-p_i)b_i+1] fi1=[pi+ai(1pi)]f0+[(1pi)bi+1] 因此 a i − 1 = p i + a i ( 1 − p i ) a_{i-1} = p_i+a_i(1-p_i) ai1=pi+ai(1pi) b i − 1 = ( 1 − p i ) b i + 1 b_{i-1} = (1-p_i)b_i+1 bi1=(1pi)bi+1

f 0 = a 0 f 0 + b 0 f_0 = a_0f_0+b_0 f0=a0f0+b0,得 f 0 = b 0 1 − a 0 f_0 = \frac{b_0}{1-a_0} f0=1a0b0

代码:

#include
using namespace std;
#define fi first
#define se second
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
const int mod = 998244353;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;

ll p[maxn], a[maxn], b[maxn];
ll qpow(ll a, ll b){
	ll res = 1;
	while(b){
		if(b & 1)
			res = res * a % mod;
		b = b >> 1;
		a = a * a % mod;
	}
	return res;
}
ll inv(int x){
	return qpow(x, mod-2);
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	int n;
	cin >> n;
	for(int i = 1; i <= n; i++){
		int x, y;
		cin >> x >> y;
		p[i] = x * inv(y) % mod;
	}
	a[n-1] = p[n], b[n-1] = 1;
	for(int i = n-1; i >= 1; i--){
		a[i-1] = (p[i]+a[i]*(1-p[i])) % mod;
		b[i-1] = ((1-p[i])*b[i]+1) % mod;
	}
	ll ans = ((b[0]*inv(1-a[0]))%mod+mod)%mod;
	cout << ans << endl;
	return 0;
}


F 青蛙过河

题意:

河里有高度 h i h_i hi 的石头,每次从石头起跳石头的高度就会下降1。具有跳跃能力 y y y 时,能跳不超过 y y y 的距离。每次上课都需要往返,询问上完 x x x 次课的最小跳跃能力

解析:

二分答案,对于跳跃能力 k k k ,如果每个长度为 k k k 的高度和都大于等于 2 x 2x 2x ,则跳跃能力 k k k 是可行的。

必要性:如果有一个长度为 k k k 的区间高度和小于 2 x 2x 2x,则必须有青蛙不经过这个区间,对跳跃能力 k k k 来说是不可能的。

充分性: 2 x 2x 2x 只青蛙第一次跳之后,所有青蛙分布在 [ 1 , k ] [1,k] [1,k] 中,第二次跳考虑将青蛙分布在 [ 2 , k + 1 ] [2,k+1] [2,k+1] 中。

如果 h 1 ≤ h k + 1 h_1 \le h_{k+1} h1hk+1 :可以将第一块石头上的所有青蛙跳到第 k + 1 k+1 k+1

如果 h 1 > h k + 1 h_1 > h_{k+1} h1>hk+1 :将第一块石头上的部分青蛙跳到第 k + 1 k+1 k+1 块石头,因为 [ 2 , k + 1 ] [2,k+1] [2,k+1] 是可以容纳 2 x 2x 2x 只青蛙的,将第一块石头上的剩余青蛙跳到 [ 2 , y ] [2,y] [2,y] 中未满的石头。

代码:

#include
using namespace std;
#define fi first
#define se second
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;

int n, x, ans;
int h[maxn], sum[maxn];
bool check(int k){
	for(int i = 1; i+k-1 <= n; i++)
		if(sum[i+k-1] - sum[i-1] < 2*x)
			return false;
	return true;
	
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n >> x;
	for(int i = 1; i < n; i++){
		cin >> h[i];
		sum[i] = sum[i-1] + h[i];
	}
	sum[n] = INF;
		
	int l = 1, r = n;
	while(l <= r){
		int mid = (l+r) >> 1;
		if(check(mid)){
			r = mid-1;
			ans = mid;
		}
		else
			l = mid+1;
	} 
	cout << ans << endl;
	return 0;
}


I 数的拆分

题意:

T T T 次询问,每次询问 a a a 是否能表示成 x 1 y 1 ⋅ x 2 y 2 x_1^{y_1} \cdot x_2^{y_2} x1y1x2y2 的形式。 ( a ≤ 1 0 18 , y 1 , y 2 ≥ 2 ) (a \le 10^{18},\quad y_1,y_2 \ge 2) (a1018,y1,y22)

解析:

a a a 质因数分解: a = ∏ i = 1 k p i q i a = \prod\limits_{i=1}\limits^{k}p_i^{q_i} a=i=1kpiqi 。需要 q i ≥ 2 q_i \ge 2 qi2
x 1 x_1 x1 中有 k 1 k_1 k1 p i p_i pi x 2 x_2 x2 中有 k 2 k_2 k2 p i p_i pi ,则 k 1 y 1 + k 2 y 2 = q i k_1y_1+k_2y_2 = q_i k1y1+k2y2=qi

容易发现, y 1 = 2 , y 2 = 3 y1 = 2,y_2 = 3 y1=2,y2=3 对所有 q i q_i qi 均有非负整数解。

所以问题转化为 a = x 1 2 x 2 3 a = x_1^2x_2^3 a=x12x23

a = x 1 2 x 2 3 ≤ 1 0 18 a = x_1^2x_2^3 \le 10^{18} a=x12x231018 。如果 p > 4000 p > 4000 p>4000 p 5 > 1 0 18 p^5 > 10^{18} p5>1018,所以只需要暴力分解 4000 4000 4000 之内的质因子即可。对于 p i > 4000 p_i>4000 pi>4000 , q i q_i qi 的取值只能为 2,3,4,只需要判断是否为平方数或者立方数

代码:

#include
using namespace std;
#define fi first
#define se second
const int maxn = 4e3+10;
const int INF = 0x3f3f3f3f;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;
#define int ll
bool notprime[maxn];
int prime[maxn], tot;
void init(int n){
	for(int i = 2; i <= n; i++){
		if(!notprime[i]){
			prime[++tot] = i;
			for(int j = 2*i; j <= n; j += i)
				notprime[j] = true;
		}
	}
}
bool check(ll x){
	ll y = pow(x, 0.5);
	if(y*y == x || (y+1)*(y+1) == x)
        return true;
    y = pow(x, 1.0/3);  
    if(y*y*y ==x || (y+1)*(y+1)*(y+1) == x)
        return true;
    return false;
}
void solve(){
	ll x;
	cin >> x;
	for(int i = 1; i <= tot; i++){
		if(x % prime[i] == 0){
			int cnt = 0;
			while(x % prime[i] == 0){
				cnt++;
				x /= prime[i];
			}
			if(cnt == 1){
				cout << "no" << endl;
				return;
			}
		}
	}
	if(check(x))
		cout << "yes" << endl;
	else
		cout << "no" << endl;
	return;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	init(4000);
	int T;
	cin >> T;
	while(T--)
		solve();
	return 0;
}


J 推导部分和

题意:

长度为 n n n 的数组 A A A ,给出 m m m 个区间和 ( l , r , s ) (l, r, s) (l,r,s), 即 ∑ i = l r A [ i ] = s \sum\limits_{i=l}\limits^r A[i] = s i=lrA[i]=s q q q 次询问 ( l , r ) (l, r) (l,r) 的区间和,即 ∑ i = l r A [ i ] \sum\limits_{i=l}\limits^r A[i] i=lrA[i]

数据范围: 1 ≤ n , m , q ≤ 1 0 5 , − 1 0 12 ≤ s ≤ 1 0 12 , 1 ≤ l ≤ r ≤ n 1 ≤ n, m, q ≤ 10^5,−10^{12} ≤ s ≤ 10^{12},1 ≤ l ≤ r ≤ n \quad 1n,m,q1051012s10121lrn数据保证没有矛盾

解析:

带权并查集。
对于区间和 ( l , r , s ) (l, r, s) (l,r,s) ,若 l l l r r r 有共同的祖先,不需要合并。如果没有共同的祖先,进行合并操作并更新 v a l val val 值。为了保证连续性,区间为左开右闭即 ( l − 1 , r ] (l-1,r] (l1,r]

查询时,如果 l − 1 l-1 l1 r r r 没有共同祖先,输出 UNKNOWN,否则输出 v a l [ r ] − v a l [ l − 1 ] val[r]-val[l-1] val[r]val[l1]

时间复杂度 O ( ( m + q ) l o g n ) O((m+q)logn) O((m+q)logn)

代码:

#include
using namespace std; 
typedef long long ll;
const int maxn = 1e5+10;
inline ll read(){//读入优化 
	char ch;
	ll sign = 1;
	while((ch=getchar())<'0'||ch>'9')
		if(ch == '-')
			sign = -1;
	ll res = ch-48;
	while((ch=getchar())>='0'&&ch<='9')
		res = res*10+ch-48;
	return res*sign;
}
ll fa[maxn], val[maxn];
ll n, m, q, l, r, s;
ll find(ll x){
	if(x != fa[x]){
		ll fx = fa[x];
		fa[x] = find(fx);
		val[x] += val[fx];
	}
	return fa[x];
}
void merge(ll x, ll y, ll z){ 
	ll fx = find(x);
	ll fy = find(y);
	fa[fy] = fx;
	val[fy] = val[x]+z-val[y];
}
void query(int x, int y){
	ll fx = find(x);
	ll fy = find(y);
	if(fx != fy)
		printf("UNKNOWN\n");
	else
		printf("%lld\n", val[y] - val[x]);
}
int main(){
	cin >> n >> m >> q;
	for(int i = 1; i <= n; i++)
		fa[i] = i;
	for(int i = 1; i <= m; i++){
		l = read(), r = read(), s = read();
		l--;
		merge(l, r, s);
	}
	for(int i = 1; i <= q; i++){
		l = read(), r = read();
		l--;
		query(l, r);
	}
	return 0;
}

你可能感兴趣的:(蓝桥杯,算法,c++,动态规划)