Codeforces Round #725 (Div. 3)
A. Stone Game
Example
input
5
5
1 5 4 3 2
8
2 1 3 4 5 6 8 7
8
4 2 3 1 8 6 7 5
4
3 4 2 1
4
2 3 1 4
output
2
4
5
3
2
题目大意:
给你一个数组(1-n每个数出现一次),你每次只能从最左或最右边删掉一个元素,求最少步数删除最大值和最小值。
思路:
贪心,先找出最大值和最小值的位置,答案就是:只从左边删,只从右边删,从左右两边删 里的最少步数。
代码:
void solve()
{
int n;
cin>>n;
int pos1,pos2;
for(int i=1;i<=n;++i)
{
int a;
cin>>a;
if(a==1)pos1=i;
if(a==n)pos2=i;
}
cout<<min(min(max(pos1,pos2),max(n-pos1+1,n-pos2+1)),min(pos1+n-pos2+1,pos2+n-pos1+1))<<endl;
}
signed main()
{
fastio();
int t;
t=1;
cin>>t;
while(t--)
{
solve();
}
return 0;
}
B. Friends and Candies
Example
input
5
4
4 5 2 5
2
0 4
5
10 8 5 1 4
1
10000
7
1 1 1 1 1 1 1
output
2
1
-1
0
0
题目大意:
给你一个数组,你可以选择k个元素,任意分配这k个元素的值给他们或者数组里的其他元素,最终使得所有元素的值都相同,求最小的k。
思路:
数学+贪心。先求出sum,不能整除当然就不可能存在答案,如果能整除,算出平均值,我们可以总是选择大于平均值的数,让他们多出来的部分配到其他小的数上,选择的元素个数就是大于平均值的数的个数。
代码:
const int N=2e5+7;
int a[N];
void solve()
{
int n;
cin>>n;
int sum=0;
for(int i=0;i<n;++i)
{
cin>>a[i];
sum+=a[i];
}
if(sum%n!=0)
{
cout<<-1<<endl;
}
else
{
int v=sum/n;
int ans=0;
for(int i=0;i<n;++i)
{
if(a[i]>v)++ans;
}
cout<<ans<<endl;
}
}
signed main()
{
fastio();
int t;
t=1;
cin>>t;
while(t--)
{
solve();
}
return 0;
}
C. Number of Pairs
Example
input
4
3 4 7
5 1 2
5 5 8
5 1 2 4 3
4 100 1000
1 1 1 1
5 9 13
2 5 5 1 1
output
2
7
0
1
题目大意:
给你一个数组,求任一对数之和在l到r范围之间有多少组。
思路:
暴力O(n*n)必 W a,所以我要相信存在某种复杂度更低的方法。
二分。因为l<=a[i]+a[j]<=r,所以l-a[j]<=a[i]<=r-a[j],所以我们可以先O(n)遍历每个元素,然后对每个元素二分(记得排序啊),复杂度是O(n*log n),注意,题目要求元素下标必须i<j,所以二分的时候范围注意一下。
代码:
const int N=2e5+7;
int a[N];
void solve()
{
int n;
int l,r;
cin>>n;
cin>>l>>r;
for(int i=0;i<n;++i)
{
cin>>a[i];
}
sort(a,a+n);
int ans=0;
for(int i=0;i<n;++i)
{
int templ=l-a[i];
int tempr=r-a[i];
ans+=upper_bound(a+i+1,a+n,tempr)-lower_bound(a+i+1,a+n,templ);
}
cout<<ans<<endl;
}
signed main()
{
fastio();
int t;
t=1;
cin>>t;
while(t--)
{
solve();
}
return 0;
}
D. Another Problem About Dividing Numbers
Example
input
8
36 48 2
36 48 3
36 48 4
2 8 1
2 8 2
1000000000 1000000000 1000000000
1 2 1
2 2 1
output
YES
YES
YES
YES
YES
NO
YES
NO
题目大意:
给三个数 a,b,k,你每次可以选择a或b除以c,其中c是你选择的数的一个约数(不能是1),问是否能恰好k次之后两数相等。
思路:
数学,数论。模拟一下几个样例就发现了,最后能否相同关键看a和b的约数个数。比如a,因为c是你随便选的,所以你甚至可以直接除以a,之后等于1,b同理,所以你至少可以2次就相等。下限有了,那么上限呢。我们分别把a,b质因数分解,因数之和就是你能除的最大次数。然后k等于1的时候特判一下,k = = 1时,若a = = b,你必须除一个数,所以最后不可能相等,若a整除b或b整除a,你总是可以除其中的一个余数使其相等。
这里用的是sqrt(n)的方法求数的约数个数,复杂度是O(n*sqrt(n)),也可以用倍数法求整个区间的数的约数个数,然后O(1)查询,会比较快。
代码:
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;
else cout<<"NO"<<endl;
}
else
{
int numa=0;
int numb=0;
for(int i=2;i*i<=a;++i)
{
if(a%i==0)
{
a/=i;
++numa;
while(a%i==0)
{
a/=i;
++numa;
}
}
}
if(a>1)++numa;
for(int i=2;i*i<=b;++i)
{
if(b%i==0)
{
b/=i;
++numb;
while(b%i==0)
{
b/=i;
++numb;
}
}
}
if(b>1)++numb;
if(k>=0&&k<=numb+numa)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
}
signed main()
{
fastio();
int t;
t=1;
cin>>t;
while(t--)
{
solve();
}
return 0;
}
E. Funny Substrings
Example
input
4
6
a := h
b := aha
c = a + b
c = c + c
e = c + c
d = a + c
15
x := haha
x = x + x
x = x + x
x = x + x
x = x + x
x = x + x
x = x + x
x = x + x
x = x + x
x = x + x
x = x + x
x = x + x
x = x + x
x = x + x
x = x + x
1
haha := hah
5
haahh := aaaha
ahhhh = haahh + haahh
haahh = haahh + haahh
ahhhh = ahhhh + haahh
ahhaa = haahh + ahhhh
output
3
32767
0
0
题目大意:
模拟一种语言,只有 x := s 和 x = a + b 这两种语句,求最后一句得出的字符串有多少个haha(可重叠计算)
思路:
根据这个语言的特点和n的取值范围,最后字符串的长度最大将近2^50,已经超出c++的存储范围了(你要是会python,直接模拟就过了…),因此我们可以先学会py再来做这题 因此我们要用到一些处理技巧。
注意到我们的模式串是"haha",长度为4,我们算出的结果没必要保存全部的字符,我们只需保存前三个字符和后三个字符就行了,当合并的时候,我们让前一个字符串的后缀与后一个字符串的前缀合并,求一次模式串的数量,再加上两个字符串之前就已经算出来的自己的模式串的数量,就是合并之后的数量。
较大的模拟题中,将各个部分的功能写成函数是一种好习惯。
代码:
map<string,int>ans;
int count(const string& s, const string& p)
{
int cnt = 0;
for (int i = 0; i + p.size() <= s.size(); i++)
{
if (s.substr(i, p.size()) == p)
{
cnt++;
}
}
return cnt;
}
string getFirst(string s)
{
if (s.size() < 3)
{
return s;
}
return s.substr(0, 3);
}
string getLast(string s)
{
if (s.size() < 3)
{
return s;
}
return s.substr(s.size() - 3, 3);
}
void solve()
{
map<string,string>ma;//记录前缀和后缀
ans.clear();
int n;
cin>>n;
int temp2=0;
for(int i=0;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;
}
temp2=ans[a];
}
cout<<temp2<<endl;
}
signed main()
{
fastio();
int t;
t=1;
cin>>t;
while(t--)
{
solve();
}
return 0;
}
F. Interesting Function
Example
input
4
1 9
9 10
10 20
1 1000000000
output
8
2
11
1111111110
题目大意:
给两个数l和r(l<r),l每次+1,问加到r 数位总共变了多少次。
思路:
看最后一个样例不难有个猜想,通过模拟题意也可以发现,从0到10需要花11次,从0到100需要花111次,所以我们可以用0到r的次数减去0到l的次数,就是l到r的次数(这其实是一种数位dp的思想)。
假设f(x)是一个函数,它可以返回x这个数的每一位都是1的数,且r=123,那么次数就等于f(1)* 3+f(10)* 2+f(100)* 1.
代码:
int qpow(int a,int b){int ans=1;while(b){if(b&1)ans=ans*a;b>>=1;a=a*a;}return ans;}
int weishu(int n)
{
int ans=0;
while(n)
{
n/=10;
++ans;
}
return ans;
}
int cal(int n)
{
if(weishu(n)==1)return n;
int ans=0;
while(n)
{
n/=10;
++ans;
}
int num=0;
while(ans--)
{
num=num*10+1;
}
return num;
}
void solve()
{
int l,r;
cin>>l>>r;
int numl=0;
int numr=0;
int xishu=0;
while(r)
{
int tempr=r%10;
r/=10;
int a=cal(qpow(10,xishu++))*tempr;
numr+=a;
}
xishu=0;
while(l)
{
int templ=l%10;
l/=10;
int a=cal(qpow(10,xishu++))*templ;
numl+=a;
}
cout<<numr-numl<<endl;
}
signed main()
{
fastio();
int t;
t=1;
cin>>t;
while(t--)
{
solve();
}
return 0;
}
G. Gift Set
Example
input
9
10 12 2 5
1 1 2 2
52 311 13 27
1000000000 1000000000 1 1
1000000000 1 1 1000000000
1 1000000000 1000000000 1
1 2 1 1
7 8 1 2
4 1 2 3
output
3
0
4
1000000000
1
1
1
5
0
题目大意:
有四个数字x,y,a,b,其中x,y分别是红色糖果和蓝色糖果的数量,你每次只能把a个红色糖果b个蓝色糖果或b个红色糖果a个蓝色糖果放进一个包裹里,问最后最多能放多少个包裹。
思路:
这题有很多做法,官方给的二分,好像还有一些大佬做成几何题,我这里用数学+贪心的方法。
我们先保证x<=y,a<=b(如果大于就交换).
当a==b时,答案就是min(x/a,y/a).
当a!=b时:
我们注意到 我们可以贪心让x全部用a装,y全部用b装,但是有另一种情况是在这样贪心之后,x和y的相对大小会变。这是因为y-x<b-a,减的时候右边变化的速率大于左边变化的速率,所以不能直接这样贪心,但是我们可以在相对大小改变之前,减去x和y相差的那部分,让x和y相近,然后x用a,y用b,x用b,y用a,一顿偶数次操作之后x和y的相对差值就不会变,最后再检查一下是否可以再包一个包裹就行了。
代码:
void solve()
{
int x,y,a,b;
cin>>x>>y>>a>>b;
if(a==b)
{
cout<<min(x,y)/a<<endl;
return;
}
if(x>y)swap(x,y);
if(a>b)swap(a,b);
int ans=0;
if(y-x>b-a)//贪心之后相对大小不会变
{
int temp=min(min((y-x)/(b-a),x/a),y/b);
x-=a*temp;
y-=b*temp;
ans+=temp;
}
int Min=min(x/(a+b),y/(a+b));//成对
ans+=Min*2;
x-=Min*(a+b);
y-=Min*(a+b);
if(x>=a&&y>=b||x>=b&&y>=a)ans++;
cout<<ans<<endl;
}
signed main()
{
fastio();
int t;
t=1;
cin>>t;
while(t--)
{
solve();
}
return 0;
}