24暑假集訓day1下午

Yantai_YZY發表於2024-08-03

下午

內容:二分分治模擬

廣告:推薦題單

1. 二分

二分查詢

優點:在檢驗一個元素之後可以很快的捨棄掉一
半的元素,從而快速鎖定目標元素。

T1 數的劃分

問題簡述:輸入 \(n\) 個不超過 \(10^9\) 的單調不減的(就是後面的數字不小於前面的數字)非負整數 \(a_1,a_2,\dots,a_{n}\),然後進行 \(m\) 次詢問。對於每次詢問,給出一個整數 \(q\),要求輸出這個數字在序列中第一次出現的編號,如果沒有找到的話輸出 \(-1\)

思路:
直接二分查詢

std:

#include <iostream>
#define int long long
#define endl '\n'
using namespace std;
const int MAXN = 1e6 + 10;
int a[MAXN];
signed main(){
	int n, m;
	cin >> n >> m;
	for(int i = 1;i <= n;i++){
		cin >> a[i];
	}
	for(int i = 1;i <= m;i++){
		int x;
		cin >> x;
		int l = 1, r = n;
		while(l < r){
			int mid = (l + r) / 2;
			if(a[mid] >= x){
				r = mid;
			} else {
				l = mid + 1;
			}
		}
		if(a[l] != x){
			cout << "-1 ";
		} else {
			cout << l << " ";
		}
	}
	return 0;
}

記錄

二分答案

用處:有最大值最小或者最小值最大的描述時可以嘗試考慮二分答案 (反正你感覺很怪就對了)

T2 跳石頭

問題簡述:給你一排 \(N\) 塊石頭,你可以移走 \(M\) 塊石頭,使得最小的兩塊石頭之間的距離儘可能長。

思路:
直接二分查詢

std:

#include<bits/stdc++.h>

using namespace std;

int l , n , m;
int a[100010];
bool check(int d) {
	int last = 0 , cnt = 0;
	for(int i = 1 ; i <= n ; i ++) {
		if(a[i] - last < d) cnt ++;
		else last = a[i];
	}
	if(l - last < d) cnt ++;
	return cnt <= m;
}
int main() {
	scanf("%d%d%d", &l , &n , &m);
	for(int i = 1 ; i <= n ; i ++) {
		scanf("%d" , &a[i]);
	}
	int le = 1 , r = l + 1 , mid;
	while(le + 1 < r) {
		mid = (le + r) / 2;
		if(check(mid)) le = mid;
		else r = mid;
	}
	printf("%d", le);
	return 0;
}

記錄


2. 分治

本質:分而治之

T3 跳石頭

問題簡述:組委會已經選擇好了兩塊岩石作為比賽起點和終點。在起點和終點之間,有 \(N\) 塊岩石(不含起點和終點的岩石)。在比賽過程中,選手們將從起點出發,每一步跳向相鄰的岩石,直至到達終點。為了提高比賽難度,組委會計劃移走一些岩石,使得選手們在比賽過程中的最短跳躍距離儘可能長。由於預算限制,組委會至多從起點和終點之間移走 \(M\) 塊岩石(不能移走起點和終點的岩石)。

思路:

std:

#include <iostream>
using namespace std;
const int MAXN = 1e6 + 10;
int a[MAXN];
int L, n , m;
bool check(int d){
	int last = 0, cnt = 0;
	for(int i = 1;i <= n;i++){
		if(a[i] - last < d) cnt++;
		else last = a[i];
	}
	if(L - last < d){
		cnt++;
	}
	return cnt <= m;
}
int main(){
	scanf("%d%d%d", &L , &n , &m);
	for(int i = 1;i <= n;i++){
		scanf("%d", &a[i]);
	}
	int l = 1 , r = L + 1 , mid;
	while(l + 1 < r){
		mid = (l + r) / 2;
		if(check(mid)){
			l = mid;
		}
		else{
			r = mid;
		}
	}
	printf("%d", l);
	return 0;
}

記錄


T4 逆序對

問題簡述:對於給定的一段正整數序列,逆序對就是序列中 \(a_i>a_j\)\(i<j\) 的有序對。知道這概念後,他們就比賽誰先算出給定的一段正整數序列中逆序對的數目。注意序列中可能有重複數字。

思路:

只要出現右側的值<左側的值的情況,就出現了逆序對。左側還剩多少個值,就會出現多少個逆序對。

再把所有分治時出現的所有逆序對的個數加起來就行了

std:

#include<bits/stdc++.h> 
#define int long long
using namespace std;
const int MAXN = 500005;
int d[MAXN];
int a[MAXN],b[MAXN],c[MAXN];
int ans = 0;
void merge(int n,int m){
	int aa = 1,bb = 1;
	int cc = 0;
	while(aa <= n && bb <= m){
		if(a[aa] <= b[bb]){
			cc ++;
			c[cc] = a[aa];
			ans += bb - 1;
			aa ++;
		}
		else{
			cc ++;
			c[cc] = b[bb];
			bb ++;
		}
	}
	while(aa <= n){
		cc ++;
		c[cc] = a[aa];
		ans += bb - 1;
		aa ++;
	}
	while(bb <= m){
		cc ++;
		c[cc] = b[bb];
		bb ++;
	}
}
void guibing(int l,int r){
	if(l >= r){
		return;
	}
	int mid = (l+r)/2;
	guibing(l,mid);
	guibing(mid+1,r);
	for(int i=l;i<=mid;++i){
		a[i - l + 1] = d[i];
	}
	for(int i=mid+1;i<=r;++i){
		b[i - mid] = d[i];
	}
	merge(mid - l + 1,r - mid);
	for(int i=l;i<=r;++i)
		d[i] = c[i - l + 1];
}
signed main(){
	int n;
	scanf("%lld",&n);
	for(int i=1;i<=n;++i){
		scanf("%lld",&d[i]); 
	}
	guibing(1,n);
	printf("%lld",ans);
}

記錄


3. 模擬

本質:按照題目的要求進行模擬操作

T5 鋪地毯

std:

#include<iostream>
using namespace std;
int a[100000], b[100000], g[100000], k[100000];
int main(){
	int x, y, n ,s=-1;
	cin >> n;
	for(int i = 0;i < n;i++){
		cin >> a[i] >> b[i] >> g[i] >> k[i];
	}
	cin >> x >> y;
	for(int i = 0;i < n;i++){
	if(x >= a[i]&&y >= b[i]&&x <= a[i] + g[i]&&y <= b[i] + k[i]){
			s = i + 1;
		}
	}
	cout << s;
}

記錄


T6 接水問題

std:

#include<cstdio>
using namespace std;
int w[10001], s[101], maxx;
int main(){
    int n, m;
    scanf("%d%d",&n,&m);
    for (int i = 1;i <= n;i++){
    	scanf("%d", &w[i]);
    }
    for (int i = 1;i <= n;i++){
        maxx = 1;
        for (int j = 2;j <= m;j++)
         if (s[maxx] > s[j]){
         		maxx = j;
         }
        s[maxx] += w[i];
    }
    maxx = 1;
    for (int i = 1;i <= m;i++){
    	maxx = s[i] > maxx ? s[i] : maxx;
    }
    printf("%d",maxx);
}

記錄


T7 神奇的幻方

問題簡述: 簡述不了

std:

#include <bits/stdc++.h>
using namespace std;
int N, a[40][40], h, l;
int main(){
	cin >> N;
	a[0][N/2] = 1, h = 0, l = N / 2;
	for(int i = 2;i <= N * N;i++){
		if(h==0&&l!=N-1) a[N-1][l+1]=i,h=N-1,l++;
		else if(l == N - 1&&h != 0) a[h - 1][0] = i,h--, l = 0;
		else if(h == 0&&l == N - 1) a[h + 1][l] = i,h++;
		else if(h != 0&&l != N - 1){
			if(a[h - 1][l + 1] == 0&&h - 1 >= 0&&l + 1 <= N){
				a[h - 1][l + 1] = i;
				h--;
				l++;
			}
			else a[h + 1][l] = i, h++;
		}
	}
	for(int i = 0;i < N;i++){
		for(int j = 0;j<N;j++){
			cout << a[i][j] << ' ';
		}
		cout << endl;
	}
}

記錄