iwtgm-3
题目链接
A.
一个数组可以头尾两端取,求取到最小值和最大值所需最小步数
从两头分别取或者从左向右取完或从右向左取完,三种情况取最小的步数即可
void solve()
{
int n;cin>>n;
int mi=150,id_mi,ma=-1,id_ma;
for(int i=1,x;i<=n;i++){
cin>>x;
if(x>ma){
ma=x;id_ma=i;
}
if(x<mi){
mi=x;id_mi=i;
}
}
if(id_ma<id_mi)swap(id_mi,id_ma);
cout<<min({id_mi+n-id_ma+1,id_ma,n-id_mi+1})<<endl;
}
B.
先算平均数,然后超过平均数的一定是要分出去的,超过平均数的个数即为答案
ll a[N];
void solve()
{
int n;cin>>n;
ll sum=0;
for(int i=1;i<=n;i++){
cin>>a[i];
sum+=a[i];
}
if(sum%n!=0){
cout<<-1<<endl;return ;
}
ll cnt=0;
sum=sum/n;
for(int i=1;i<=n;i++){
if(a[i]>sum)cnt++;
}
cout<<cnt<<endl;
}
C.
求序列和在l-r范围内的子序列的个数
把数组排序,然后对于每一个数去找满足条件的最小值和最大值,两者之差+1即为该数的贡献
推荐用lower_bound()和upper_bound()代码少
自己手写的二分错了,后来队友帮忙把二分刚好满足条件退出的break去掉也过了,特别鸣谢@north_
简洁版:
ll a[N];
void solve()
{
memset(a,0,sizeof(a));
ll n,l,r;cin>>n>>l>>r;
for(int i=0;i<n;i++)cin>>a[i];
sort(a,a+n);
ll ans=0;
for(int i=0;i<n;i++){
ll l0= lower_bound(a+1+i,a+n,l-a[i])-a;
ll r0= upper_bound(a+1+i,a+n,r-a[i])-a;
ans+=r0-l0;
}
cout<<ans<<endl;
}
手写二分版:
void solve()
{
memset(a,0,sizeof(a));
ll n,l,r;cin>>n>>l>>r;
for(int i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+1+n);
ll ans=0;
for(int i=1;i<n;i++){
ll x=a[i];
ll left=i+1,right=n,mid;
ll idx_left=0,idx_right=0;
while(left<=right){
mid=(left+right)/2;
if(x+a[mid]<l){
left=mid+1;
}
else {
idx_left=mid;right=mid-1;
}
}
left=idx_left;right=n;
if(idx_left==0||x+a[idx_left]>r)continue;
while(left<=right){
mid=(left+right)/2;
if(x+a[mid]>r){
right=mid-1;
}else {
idx_right=mid;left=mid+1;
}
}
if(idx_right==0||x+a[idx_right]<l)continue;
//cout<<"idx_left="<<idx_left<<' '<<"idx_right="<<idx_right<<endl;
ans+=idx_right-idx_left+1;
}
cout<<ans<<endl;
}
D.
给两个数,操作是能把该数替换成任何一个它的因数,问能否刚好k次操作让两个数相等
k=0,两个数必须相等
k=1,两个数必须存在倍数关系
其他情况我们把两个数的质因数个数算出来,>k,则满足条件<k,则不满足条件
之前自己的想法是分类讨论了质数和质数,合数和合数,质数和合数
因为数据范围是1e9,筛不了这么大的质数就放弃了
但是按循环跑如果出一个1e9左右范围的质数那也要跑1e9啊,数据水了?
int count(int x,int k){
for(int i=2;i*i<=x;i++){
while(x%i==0){
x/=i;
k--;
}
}
if(x>1)k--;
return k;
}
void solve()
{
int a,b,k;cin>>a>>b>>k;
if(k==1){
if((a%b==0||b%a==0)&&a!=b){
cout<<"YES"<<endl;return ;
}else {
cout<<"NO"<<endl;return ;
}
}
k=count(a,k);
k=count(b,k);
cout<<(k>0?"NO":"YES")<<endl;
}
G.
让n<m,a<b,首先如果一个礼盒都凑不齐就输出0
特判a==b的情况,答案为n/a
基本原则是让大的取大的,但直接模拟会超时,于是考虑:
先取一些(a,b)这样的二元组,再取一些(a+b,a+b)这样的二元组最优,
证明:
选一个二元组(a,b)会让(n,m)之间的差缩减(b-a),
当第一次让n>m时,之后m和n的差总小等于a和b的差,也就是大小关系会反复换过来
所以我们可以先选(m − n)/(b-a)个(a,b),向下取整和多取一个两种情况
同时要考虑到n最多有多少个a,m最多有多少个b,三者取一个最小值
剩下的可以全选(a+b,a+b)
取两种情况两个值的较大值
void solve()
{
int x,y,a,b;cin>>x>>y>>a>>b;
if(x>y)swap(x,y);
if(a>b)swap(a,b);
if(x<a||y<b){
cout<<0<<endl;return ;
}
int res=y-x,lim=b-a;
if(!lim){
cout<<min(x,y)/a<<endl;return ;
}
int cur=min(res/lim,min(x/a,y/b));
x-=cur*a;y-=cur*b;
int ans=cur+min(x,y)/(a+b)*2;
if(x>=a&&y>=b){
x-=a;y-=b;cur++;//位置1
}
cout<<max(ans,cur+min(x,y)/(a+b)*2)<<endl;
}
F.
l和r,分别算有多少个1,多少个10,多少个100...
然后再相减
ll base[15]={1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};
void solve()
{
ll l,r;cin>>l>>r;
ll ans=0,res=0;
for(int i=0;i<10;i++){
ans+=r/base[i];
res+=l/base[i];
}
cout<<(ans-res)<<endl;
}
E.
输入掉大分,字符串里有空格,直接分成多个字符串
计算单个字符串的贡献,
有链接操作数据量太大,可以只保留开头和结尾,中间已经算过贡献的舍去
string s;
map<string,string>ma;
map<string,ll>ans;
string getfirst(string x){
if(x.size()<3)return x;
else return x.substr(0,3);
}
string getlast(string x){
if(x.size()<3)return x;
else return x.substr(x.size()-3,3);
}
ll count(string a,string b){
ll cnt=0;
for(int i=0;i+b.size()<=a.size();i++){
if(a.substr(i,b.size())==b)++cnt;
}
return cnt;
}
void solve()
{
ma.clear();ans.clear();
ll n;cin>>n;
ll tmp=0;
for(int i=1;i<=n;i++){
string a,b,c,d,e;cin>>a>>b>>c;
if(b==":="){
ans[a]= count(c,"haha");
ma[a]=c;
}else{
cin>>d>>e;
string f=ma[c]+ma[e];
ans[a]=ans[c]+ans[e]+count(getlast(ma[c])+getfirst(ma[e]),"haha");
if(f.size()>=7)f= getfirst(f)+"@"+getlast(f);
ma[a]=f;
}
tmp=ans[a];
}
cout<<tmp<<endl;
}