习题一:
题目传送门:
https://codeforces.com/gym/103688/problem/A
题意:
有n本高度为a的A类数和m本高度为b的B类书,他们的厚度都是1,最初A类书都在左边,B类书都在右边,其宽度是n+m,选取k本B类书放置A类书的上面以缩短宽度,求最小宽度是多少。
思路:
二分答案
check函数中,将宽度x认为已知,判断其是否符合条件,若宽度为x,则拿出来的书就是n+m-x,再看这写书的放置是否合法即可。
代码显示如下:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 3e5 + 10;
int n,m,a,b,h;
bool check(int x)
{
int p=n+m-x;
int h1=h-a;
if((n/b)*h1>=p) return true;
p-=(n/b*h1);
int h2=h-b;
if((x+n%b-n)/b*h2>=p) return true;
return false;
}
void solve() {
cin>>a>>b>>n>>m>>h;
int l=n+1,r=n+m;//注意k是[0,m-1]
while(l<r)
{
int mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid+1;
}
cout<<l<<endl;
}
signed main() {
int t = 1;
cin>>t;
while (t--) {
solve();
}
}
习题二:
题目传送门:
https://codeforces.com/contest/1846/problem/E2
题意:
有一个顶点,有k个新顶点与其相连接,可执行无限次以下操作:让最外的每个顶点通过边与k个新顶点相连接(此操作至少做一次),问是否存在k在执行上述操作后,使得顶点总和为n。(数据范围:1e18)
思路:遍历层数,等比数列求和,二分。
首先按层数遍历,层数最小是三,有题意可知k最小值是2,以2为公比的等比数列的求和公式是(2^x-1)要小于等于1e18,此时这个x代表层数最大值,求得最大层数近似64,所以层数x的范围是3~64。接下来就是求对每个层数对应的顶点总和是否与n相等:
已知等比数列的求和公式是:
x即是对应层数,现在目的就是求k,若直接利用求和公式开根号求解会存在精度问题,所以为了得到准确k就需要进行二分。
代码显示如下:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 3e5 + 10;
int n, m, a, b, h;
//sum<=(q^x-1)/(q-1)->sum*(q-1)+1<=q^x
bool check(int x) {
int l=2,r=1e9;
while(l<=r)
{
int mid=(l+r)/2;
__int128 a=(__int128)n*(mid-1)+1;//会爆int,用__int128
__int128 q=1;
for(int i=1;i<=x;i++)
{
q*=mid;
if(q>a) break;
}
if(q==a) return true;
if(q>a) r=mid-1;
else l=mid+1;
}
return false;
}
void solve() {
cin >> n;
for (int i = 3; i <= 64; i++) {
if (check(i)) {
cout<<"YES"<<endl;
return;
}
}
cout<<"NO"<<endl;
}
signed main() {
int t = 1;
cin >> t;
while (t--) {
solve();
}
}
习题三:
题目传送门:
https://codeforces.com/problemset/problem/1840/D
题意:
一个大小为n的数组,a[i]表示i个人想要的图案样式,有三个雕刻师傅,一个制作图案样式为y的师傅制作一个图案样式x需要的时间是|x-y|,雕刻师傅可以同时完成不同的图案,求顾客最佳最长等待时间。
题解:
本题主要查找最佳时间,所以二分的是时间,check函数中,假设时间是x,判断是否至多三个雕刻师让其最佳最长时间是x,此为二分边界。
代码显示如下:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int n, m, a[N], b, h;
bool check(int x) {
int peo=1;
int m=a[1]+x;
for(int i=2;i<=n;i++)
{
if(abs(a[i]-m)>x) peo++,m=a[i]+x;
}
return peo<=3;
}
void solve() {
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
sort(a+1,a+1+n);
int l=0,r=1e9;
while(l<r){
int mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid+1;
}
cout<<l<<endl;
}
signed main() {
int t = 1;
cin >> t;
while (t--) {
solve();
}
}
习题四:
题目传送门:
https://codeforces.com/gym/104417/problem/D
题意:
题解:
贪心+二分
根据题意我们可以知道,如果a队员的体重小于等于b队员的体重,则b队员背a队员时,其速度不变,若a队员的体重大于b队员的体重,则b队员的速度变为v[b]-(w[a]-w[b])=(v[b]+w[b])-w[a]。队员的速度取决与(v[b]+w[b])和-w[a],最佳速度匹配就是最大的(v[b]+w[b])与最小-w[a]相配,才能使整体速度达到最大,但是如何区分哪些队员是要被背起来,哪些队员是要背人的呢?这个分界线就是最大速度,这时就需要进行二分,假定最大速度是mid,那么速度大于等于mid的就是要背人的,即(v[b]+w[b]),速度小于mid的就是要被背的,即(-w[a]),将他们分别存到dn和up数组里,dn降序排序,up升序排序,最后两两相加看是否合法即可。
代码显示如下:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 10;
int n, m, h;
typedef pair<int, int> PII;
PII a[N];
///体重小于or相等 v不变 体重大于 v[i]-(w[j]-w[i])=(v[i]+w[i])-w[j]
vector<int>up, dn;
bool check(int x) {
up.clear(),dn.clear();
for (int i = 1; i <= n; i++) {
if (a[i].first >= x) dn.push_back(a[i].first + a[i].second);
else up.push_back(-a[i].second);
}
if (up.size() > dn.size()) return false;
sort(up.begin(), up.end());
sort(dn.begin(), dn.end(), greater<int>());
for (int i = 0; i < up.size(); i++) {
if (up[i] + dn[i] < x) return false;
}
return true;
}
void solve() {
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i].first >> a[i].second;
int l = 1, r = 1e9;
while (l < r) {
int mid = (l + r + 1) >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
cout << l << endl;
}
signed main() {
int t = 1;
cin >> t;
while (t--) {
solve();
}
}