A. Setting up Camp
a,必须要一个人住。
b,必须要三个人住。
c,无所谓(1~3人)
问最少要几个帐篷。
a一定要一个人住,所以最少a个帐篷。
b要三个人住,如果b不是三的倍数,就从c那边拿人过来,帐篷再加b/3
如果c是负数,则无法完成分配,输出负一。
不是负数就把c全部安排三人间,有余数再加1。
#include <bits/stdc++.h>
#define int long long
#define per(i,j,k) for(int (i)=(j);(i)<=(k);++(i))
#define rep(i,j,k) for(int (i)=(j);(i)>=(k);--(i))
#define debug(a) cout<<#a<<"="<<a<<endl
#define all(x) x.begin(),x.end()
#define EX exit(0)
#define fr first
#define se second
#define endl '\n'
using namespace std;
using ll=long long;
void solve(){
int a,b,c;
cin>>a>>b>>c;
int ans=a;
while(b%3!=0){
b++;
c--;
}
if(c<0){
cout<<-1<<endl;
}else{
ans+=b/3;
ans+=c/3;
if(c%3)ans++;
cout<<ans<<endl;
}
}
signed main(){
ios::sync_with_stdio(false),cin.tie(nullptr);
int t=1;
cin>>t;
while(t--)solve();
return 0;
}
B. Fireworks
a烟花启动之后,每隔a分钟都会发射一次,持续m时间,即 [a,a+m],都有烟花。
b烟花启动之后,每隔b分钟都会发射一次,持续m时间,即 [b,b+m],都有烟花。
a,b烟花同时发射,同一时间最多看到几个烟花。
因为是同时发射的,所以定死了区间是 [1,m],只需要考虑 m 里面 a 和 b 能发射几次即可。
#include <bits/stdc++.h>
#define int long long
#define per(i,j,k) for(int (i)=(j);(i)<=(k);++(i))
#define rep(i,j,k) for(int (i)=(j);(i)>=(k);--(i))
#define debug(a) cout<<#a<<"="<<a<<endl
#define all(x) x.begin(),x.end()
#define EX exit(0)
#define fr first
#define se second
#define endl '\n'
using namespace std;
using ll=long long;
void solve(){
int a,b,m;
cin>>a>>b>>m;
cout<<m/a+1+m/b+1<<endl;
}
signed main(){
ios::sync_with_stdio(false),cin.tie(nullptr);
int t=1;
cin>>t;
while(t--)solve();
return 0;
}
C. Left and Right Houses
每个房子为0或者1,0011111这样就是一排房子,你需要在其中修一条路,放在任意的房子 i 之后。假设路修在 i 之后,需要满足条件:
1、左侧至少有 i/2 个0
2、右侧至少有 (n-i)/2 个1(两个条件除法均向上取整)
所有合法的位置 i,输出使得 |n/2 - i| 最小的位置。
对于任意的位置,左侧的0的数量用前缀和来维护,右侧的1用后缀和来维护,就可以O(1)访问。
满足情况的位置全部存下来,最后再对 |n/2 - i| 进行判断。(注意这里不是整除,n/2要用double类型,否则会产生错误)
#include <bits/stdc++.h>
//#define int long long
#define per(i,j,k) for(int (i)=(j);(i)<=(k);++(i))
#define rep(i,j,k) for(int (i)=(j);(i)>=(k);--(i))
#define debug(a) cout<<#a<<"="<<a<<endl
#define all(x) x.begin(),x.end()
#define EX exit(0)
#define fr first
#define se second
#define endl '\n'
using namespace std;
using ll=long long;
void solve(){
int n;
cin>>n;
string s;
cin>>s;
s="0"+s;
int len=s.length()-1;
int pre[len+5],bac[len+5];
per(i,1,len)pre[i]=bac[i]=0;
//左侧[0,i]
//右侧[i+1,len]
if(s[1]=='0')pre[1]=1;
per(i,2,len){
pre[i]=pre[i-1];
if(s[i]=='0')pre[i]++;
}
if(s[len]=='1')bac[len]=1;
rep(i,len-1,1){
bac[i]=bac[i+1];
if(s[i]=='1')bac[i]++;
}
int ans=INT_MAX,idx=INT_MAX;
pre[0]=0;
bac[0]=bac[1];
bac[len+1]=0;
vector<int>v;
per(i,0,len){
if(pre[i]>=i/2+i%2 and bac[i+1]>=(n-i)/2+(n-i)%2){
v.push_back(i);
}
}
double cul=abs((double)n/2-v[0]);
int ansidx=v[0];
per(i,1,v.size()-1){
if(abs((double)n/2-v[i])<cul){
cul=abs((double)n/2-v[i]);
ansidx=v[i];
}
}
cout<<ansidx<<endl;
}
signed main(){
ios::sync_with_stdio(false),cin.tie(nullptr);
int t=1;
cin>>t;
while(t--)solve();
return 0;
}
D. Seraphim the Owl
1 2 3 4 5 6 7 8 9 10 n n+1
现在主角站在 的位置,他需要插队到前 m 的位置。
设主角当前在 ,他可以选择任意一个
,支付
来交换位置;同时给所有的
,支付
(就是夹在他们俩中间的所有人)。
问主角插队到前m,最少要花多少钱。
1 2 3 4 5 6 7 8 9 10 n n+1
首先模拟几下,主角从 n+1,到 n,只有一种方法;但是从 n+1 到 10,可以交换 n,在交换到10,或者直接从 n+1 交换到10,并且再付出 b[10] 的代价。这样看显然就是一道dp了。
定义 为到达
位置需要的最少花费
第一种:从 直接跳过来。
即:
第二种:从后面的 ∈(
~
)最少花费转移过来。
即:
显然每次转移, 数组的花费都是一串连续的和,所以可以对
数组求一次前缀和加快访问。
将式子优化为:
①
② ∈
得出代码
dp[n]=a[n];
rep(i,n-1,1){
dp[i]=a[i]+pre[n]-pre[i];
per(x,i+1,n){
dp[i]=min(dp[i],a[i]+pre[x-1]-pre[i]+dp[x]);
}
}
但是题目中 n=2e5,n*n的复杂度过不了,外层循环已经不能优化了,考虑降低内层循环的复杂度。
观察式子,我们把常量提出来。
dp[i]=min(dp[i],a[i]+pre[x-1]-pre[i]+dp[x]);
->
dp[i]=min(dp[i],a[i]-pre[i]+min(pre[x-1]+dp[x]));
发现pre[x-1]和dp[x]均要后面的最小值,所以用变量来存pre[x-1]+dp[x]的最小值即可。
#include <bits/stdc++.h>
#define int long long
#define per(i,j,k) for(int (i)=(j);(i)<=(k);++(i))
#define rep(i,j,k) for(int (i)=(j);(i)>=(k);--(i))
#define debug(a) cout<<#a<<"="<<a<<endl
#define all(x) x.begin(),x.end()
#define EX exit(0)
#define fr first
#define se second
#define endl '\n'
using namespace std;
using ll=long long;
void solve(){
int n,m;
cin>>n>>m;
int a[n+1],b[n+1];
per(i,1,n)cin>>a[i];
per(i,1,n)cin>>b[i];
int pre[n+1];
per(i,1,n){
if(i==1)pre[i]=b[i];
else pre[i]=pre[i-1]+b[i];
}
int dp[n+1];
int MIN=LONG_LONG_MAX;
dp[n]=a[n];
rep(i,n-1,1){
dp[i]=a[i]+pre[n]-pre[i];
int BASE=a[i]-pre[i];
MIN=min(dp[i+1]+pre[i],MIN);
dp[i]=min(dp[i],BASE+MIN);
}
int mincost=LONG_LONG_MAX;
per(i,1,m)mincost=min(mincost,dp[i]);
cout<<mincost<<endl;
}
signed main(){
ios::sync_with_stdio(false),cin.tie(nullptr);
int t=1;
cin>>t;
while(t--)solve();
return 0;
}
补题:E. Binary Search
给你一个排列,对他进行二分查找,要求最终 p[l]=x。
可以在查找前进行操作(最多2次),交换任意下标的元素。
观察二分退出的条件是r=l+1
也就是 l-----------> l r <-------------r,从外围到里面,然后退出了。
假设他们走过了一些m
l--m1----m2---> l r <---m3----m4----r
一定有:m1,m2<=x;m3,m4>x
因为最终 x 要在 l 的位置,所以我们考虑能否直接将 x 挪到 l 上。
如果 x 不在 m1,m2,m3,m4上,显然不会对二分产生影响,直接交换 l 和 x 的位置。
如果 x 在 m1,m2 上,首先还是需要交换 x 和 l,若 p[l]<=x 无影响,可以随意交换。
若 p[l]>x 则交换后会对二分查找产生影响,但是观察代码 p[m]<=x,才会有 l=m,所以 l 的落点和经过的位置,一定有 p[m]<=x,即此情况不存在,直接交换无影响。
再考虑 x 在 m3,m4上,因为 r 要往左走,一定有 p[m]>x,所以 x 不会在 m3 和 m4 上。
故总结,如果二分查找结束,p[l]!=x,我们只需要交换一次 l 和 x。
#include <bits/stdc++.h>
//#define int long long
#define per(i,j,k) for(int (i)=(j);(i)<=(k);++(i))
#define rep(i,j,k) for(int (i)=(j);(i)>=(k);--(i))
#define debug(a) cout<<#a<<"="<<a<<endl
#define all(x) x.begin(),x.end()
#define EX exit(0)
#define fr first
#define se second
#define endl '\n'
using namespace std;
using ll=long long;
void solve(){
int n,x,xIDX;
cin>>n>>x;
int p[n+1];
per(i,1,n){
cin>>p[i];
if(p[i]==x)xIDX=i;
}
int l=1,r=n+1;
while(r-l!=1){
int m=(r+l)>>1;
if(p[m]<=x)l=m;
else r=m;
}
if(p[l]==x){
cout<<0<<endl;
}else{
cout<<1<<endl;
cout<<xIDX<<" "<<l<<endl;
}
}
signed main(){
ios::sync_with_stdio(false),cin.tie(nullptr);
int t=1;
cin>>t;
while(t--)solve();
return 0;
}