AtCoder Beginner Contest 388(补题)

AtCoder Beginner Contest 388(补题)

整体如果比上次多做一个题叫进步的话,那还是有点进步的。

C - Various Kagamimochi

https://atcoder.jp/contests/abc388/tasks/abc388_c

思路(Trick)

因为序列是有序的,所以从左到右枚举每一个 i ,找到第一个大于等于 2*i 的数,此数和右边所有的数都满足条件。简单计数即可。

代码1:二分法 O(n * log n)

#include <bits/stdc++.h>

typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;

void solve(){
	int n;
	std::cin >> n;

	std::vector<i64> v(n+1);
	for (int i = 1; i <= n; i++) std::cin >> v[i];

	i64 ans =  0;

	for (int i = 1; i <= n; i++){
		int a = v[i];
		int pos = std::lower_bound(v.begin()+1, v.end(), a * 2) - v.begin();
		ans += n - pos + 1;
	}

	std::cout << ans << '\n';
}

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
	int t = 1, i;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}

代码2:双指针 O(n)

#include<bits/stdc++.h>
using namespace std;
const int N=6e5+5,N1=3e5+5;
int n,a[N],s,t,f[N],dis[N],is[N];
vector<int>ve[N];
bool vis[N];
/*int gcd(int x,int y){//最大公因数
    if(x%y==0)return y;
    else return gcd(y,x%y);
}*/void init(int n){
    for(int i=2;i <=n; i++)
        is[i]= true;
    for(int i=2;i<=n;i++){
        if(!is[i])continue ;
        for(int j=i*2;j<= n;j += i){
            is[j]= false;
        }
    }
}
int main(){
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    sort(a + 1, a + 1 + n);
    int l = 1, r = 1;
    long long ans = 0;
    while (l <= n) {
        while (r <= n && a[r] < a[l] * 2) r++;
        ans += n - r + 1;
        l++;
    }
    cout << ans << endl;
    return 0;
}

D - Coming of Age Celebration

https://atcoder.jp/contests/abc388/tasks/abc388_d

思路(差分)

此题分为接收石头和传递石头两个过程,考虑接收的石头的时候要知道左边的石头是怎样传递的,所以我们直接思考传递石头。

假设 i 位置在接收完石头后有 a_i 个,则说明他要向右传递 min(a_i, n - i)个石头,一个位置一个,最终结果是对一个区间做了+1操作。但是我们在计算一个位置向右传递多少石头时需要先接收石头,接收石头很明显对应差分的结算操作,结算完后又要向右修改,但是差分只能等所有修改都结束后才能结算,所以差分貌似是不可行的。我们再仔细思考一下。

差分的确是要修改完后再通过前缀和结算,但是这里的情况是,当我们使用前缀和结算到一个位置时,他的值已经确定了,不会再改变,所以是满足差分的性质的。

所以这道题的做法就是边用差分边修改边结算。

评述

本次最大遗憾,明明想到了差分但是把题想成了边改边结算的题,所以没有实现。

代码

#include <bits/stdc++.h>

typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;

void solve(){
	int n;
	std::cin >> n;

	std::vector<int> v(n+1);
	for (int i = 1; i <= n; i++) std::cin >> v[i];

	std::vector<int> d(n+1);
	for (int i = 1; i <= n; i++){
        // 前缀和
		d[i] += d[i-1];
		v[i] += d[i];

        // 差分
		int t = std::min(v[i], n-i);
		v[i] -= t;
		d[i+1] += 1;
		d[i+1+t] -= 1;

		std::cout << v[i] << ' ';
	}	
}

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
	int t = 1, i;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}

E - Simultaneous Kagamimochi

https://atcoder.jp/contests/abc388/tasks/abc388_e

思路(二分)

假设同时做 i 个蛋糕可行,那么同时做 i - 1 个蛋糕很明显是可行的,所以这道题满足单调性,可以使用二分法。现在要解决的无非就是check函数。

我们可以贪心的想,对于一个序列我们能做 i 个蛋糕,做我们一定是拿这个序列最小的 i 个数和最大的 i 个数做尝试。接下来看代码就行。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e6 + 5;
int d[maxn], a[maxn];
int n;
bool check(int x) {
    for (int i = 1; i <= x; i++) {
        if (a[i] * 2 > a[n - x + i]) return false;

    }
    return true;
}


int main(){

    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    sort(a + 1, a + 1 + n);
    int l = 0, r = n / 2;
    while (l < r) {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    cout << l << endl;

    return 0;
}
posted @ 2025-01-11 23:31  califeee  阅读(253)  评论(0)    收藏  举报