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;
}