Acwing蓝桥杯集训·题解 week2
农夫约翰最喜欢的操作
分几步来:
-
要满足\(a_i-x\)整除\(x\)
转化一下为,即满足\(a_i \equiv x \pmod M\) ,所以预处理,\(a_i=a_i \mod M\) -
由第一步,我们可以知道\(x\in (0,M-1)\)
-
根据题意我们所求值 \(val=\sum_{i=1}^{i=n}|a_i-x|\)
当val取最小值时,由于中位数定理,x是序列\(a_i\)的中位数 -
由于同余的性质,所以\(a_i \equiv a_i+M \pmod M\)
- 所以x可以取不同的\(a_i\),就会有不同的序列,因此我们取每一个序列的中位数,比较每个序列的val,取最小
- 当x取不同的\(a_i\)时,应该以\(a_i\)为中心建立一个序列,通过取余,将两边的\(a\)数量平衡
- 为了方便这样处理可以\(a_i+M\)个数加到原序列之后,然后用前缀和快速求解
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,x,k,m;
const int maxn=4e5+10;
int t;
int w[maxn];
ll sum[maxn];
void solve(){
cin>>n>>m;
for(int i=1;i<=n;++i) cin>>w[i],w[i]%=m;
sort(w+1,w+1+n);
for(int i=1;i<=n;++i) w[i+n]=w[i]+m;
for(int i=1;i<=n*2;++i) sum[i]=sum[i-1]+w[i];
ll res=1e18;
for(int l=1;l<=n;++l){
int r=l+n-1,p=(l+r)>>1;
ll lt=(p-l+1)*(ll)w[p]-(sum[p]-sum[l-1]);
ll rt=(sum[r]-sum[p])-(r-p)*(ll)w[p];
res=min(res,lt+rt);
}
cout<<res<<"\n";
return ;
}
signed main(){
cin.tie(0);cout.tie(0);
cin>>t;
while(t--){
solve();
}
return 0;
}
拐杖糖的盛宴
你会发现,写暴力就能过,很奇妙对吧
这题主要看你理解时间复杂度
- 如果糖一直比第一个牛高,那么最多30次吃糖,之后的每一个糖都会被第一个牛吃掉
因为牛每次都翻倍,\(2^{30}>10^9\) - 如果糖不比第一个牛高,那么每次吃糖,只有低第一头牛吃
综上,时间复杂度\((Nlog{10^9})\)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
const int maxn=2e5+10;
int a[maxn],b[maxn];
int nex[maxn];
int m;
signed main(){
cin>>n>>m;
int l=1;
for(int i=1;i<=n;++i) {
cin>>a[i];
}
for(int i=1;i<=m;++i){
cin>>b[i];
int now=0;
for(int j=1;j<=n;++j){
if(a[j]<b[i]){
if(now<a[j]){
int tx=now;
now=a[j];
a[j]+=(a[j]-tx);
}
}
else {
a[j]+=(b[i]-now);
break;
}
}
}
for(int i=1;i<=n;++i) cout<<a[i]<<" \n";
return 0;
}
密接牛追踪2
其实题意很直接的,天数越长,所需的牛越少
我们只需要找到最小的那个区间感染完,所需的天数,然后以此来推出其他区间感染所需的最少的牛
设每个区间\([i,j]\)感染完的最长时间为d,易得d最大时\(d=\lceil \frac{i-j}{2} \rceil\)此时区间的最中间的一个头牛(如果不懂向上取整,就举几个例子)
此时我们得到最长的天数是r,设\(c_i\)为第i个感染牛的区间的数量,c的长度是x
所以需要的最少的牛\(\sum_{i=1}^{x} \lceil \frac{c_i}{2*r+1} \rceil=\lfloor \frac{c_i+2*r}{2*r+1}\rfloor\)
\(\lceil \frac{a}{b} \rceil= \lfloor \frac{a+b-1}{b} \rfloor\)
这个的证明,分别考虑a和b是否整除b即可,你会发现结果是一样的
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n;
string s;
vector<int>t;
int sum=0;
int main(){
cin>>n;
cin>>s;
s=" "+s;
int r=n;
for(int i=1;i<=n;++i){
if(s[i]=='0') continue;
int j=i;
while(s[i+1]=='1' && i+1<=n) ++i;
int c=i-j+1,d=(c-1)/2;//d是最小天数
if(j==1 || i==n)
d=c-1;
r=min(r,d);
t.push_back(c);
}
int res=0;
for(auto c:t){
res+=(c+r*2)/(2*r+1);//上下取整的转化
}
cout<<res<<"\n";
return 0;
}
农夫约翰真的种地
看起来有点唬人,你如果注意到\(t_i\)是个排列,那么你可以将每个竹子按照\(t_i\)排序,设过了x天,竹子高度\(h_i^{`}=h_i+xa_i\),解不等式,求交集
注意移项时,不等式会不会变号
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int t;
int n;
const int maxn=2e5+10;
int h[maxn],a[maxn];
int rk[maxn];
void solve(){
cin>>n;
for(int i=1;i<=n;++i) cin>>h[i];
for(int i=1;i<=n;++i) cin>>a[i];
for(int i=1;i<=n;++i){
int x;cin>>x;
rk[x+1]=i;
}
bool book=0;
int l=0,r=1e9;
for(int i=1;i<n;++i) {
int x=h[rk[i]]-h[rk[i+1]];
int y=a[rk[i+1]]-a[rk[i]];
if(y>0) r=min(r,(int)(ceil)(x*1.0/y)-1);
else if(y<0){
l=max(l,(int)(floor)(x*1.0/y)+1);
}
else if(x<=0){
r=-1;
break;
}
}
if(r<l) {
puts("-1");return ;
}
cout<<l<<endl;
}
int main(){
cin>>t;
while(t--){
solve();
}return 0;
}
多数意见
可以任意的选择,还可以重复的选,那么就很简单了
- 只要两个相邻的相同,那么就可以扩展相邻的一个一个变成相同的喜欢
- 如果两个相同的隔了一个,那么就也可以把中间的变成相同的喜欢,然后和1一样,全部变成相同的
综上,只要两个相同的距离不超过1就可以变成全部相同的
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int t;
int n;
const int maxn=1e5+10;
bool book[maxn];
int a[maxn];
bool check=0;
void solve(){
cin>>n;
check=0;
memset(book,0,sizeof(book));
for(int i=1;i<=n;++i) cin>>a[i];
for(int i=1;i<=n;++i){
if(a[i+2]==a[i] && i<=n-2)
book[a[i]]=1,check=1;
if(a[i]==a[i+1] && i<=n-1) book[a[i]]=1,check=1;
}
if(check){
for(int i=1;i<=n;++i)
if(book[i]) cout<<i<<" ";
cout<<endl;
}
else puts("-1");
return ;
}
int main(){
cin>>t;
while(t--){
solve();
}
return 0;
}

浙公网安备 33010602011771号