Codeforces-Div3-653(A~E1)
A. Required Remainder
题意:
\(T\)组询问,给定\(x,y,n (2≤x≤10^9; 0≤y<x; y≤n≤10^9)\),求最大的整数\(k\),满足\(0≤k≤n且k mod x=y\)
思路:
转换公式 \(k=k1*x+y,k1∈R\) ,则需要找到最大的\(k1\),使得\(k1*x+y<=n\),则\(k1_{max}=(n-y)/x\),套入第一个式子即可。
Code:
int main(){
int t;
t=read();
while(t--){
ll x,y,n;
scanll3(x,y,n);
printf("%lld\n",(n-y)/x*x+y);
}
return 0;
}
B. Multiply by 2, divide by 6
题意:
\(T\)组询问,给定一个\(n,1≤n≤10^9\),每次操作将\(n\)乘\(6\)(能被\(6\)整除),或者将\(n\)乘\(2\)。问最少多少次操作可以将\(n\)变换乘\(1\)。否则输出\(-1\)。
思路:
暴力+贪心,当前能除\(6\)就除,否则乘2,\(-1\)的情况在暴力的情况下设置上界是不会超时的。然而正确的解法是数学,这里不做描述。
Code:
int main(){
int t;
t=read();
while(t--){
ll n;
n=read();
int f=0;
if(n==1){
puts("0");
continue;
}
int cnt=0;
while(n!=1&&n<=1e9){//n<=1e9上界
if(n%6==0){
n/=6;
cnt++;
}
else {
n*=2;
cnt++;
}
}
if(n!=1)puts("-1");
else printf("%d\n",cnt);
}
return 0;
}
C. Move Brackets
题意:
\(T\)组询问,每组给定一个长度为\(n\)的括号字符串,一个括号字符串合法定义如下:
- \("()"\) is regular bracket sequence;
- if \(s\) regular bracket sequence then \("(" + s+")"\) is regular bracket sequence;
- if \(s\) and \(t\) are regular bracket sequences then \(s\) + \(t\) is regular bracket sequence.
每次移动可以将一个括号移动至头部或者尾部,问最少多少次移动使得给定字符串合法。输入满足,\(n\)是偶数,有\(\frac{n}{2}\)个\()\),\(\frac{n}{2}\)个\((\)。
思路:
先做括号匹配,考虑需要移动的情况,需要移动的字符串一定是\()))...(((\)的形式,那么做法就很显然了,正常做括号匹配,需要移动的次数就是最后栈大小的一半。
Code:
int main(){
int t;
t=read();
while(t--){
ll n;
n=read();
stack<char>Q;
string s;
cin>>s;
rep(i,0,n-1){
if(Q.empty()){
Q.push(s[i]);
continue;
}
char tp=Q.top();
if(s[i]==')'&&tp=='('){
Q.pop();
continue;
}
Q.push(s[i]);
}
cout<<Q.size()/2<<"\n";
}
return 0;
}
D. Zero Remainder Array
题意:
给一个长度为\(n\)的数组\(a\),起初,有一个整数\(x=0\),每一次操作可以选择将数组中的一个数\(a_i,a_i=ai+x\),随后\(x=x+1\),或者只执行\(x=x+1\),问需要最少多少次操作使数组\(a\)中的数都能整除\(k\)。
思路:
可以看出,每个数\(a_i\)需要加上某个\(x_i\),才能被\(k\)整除。前\(k\)次操作,可以使得那些只出现了一次\(x_i\)的全部与之对应的\(a_i\)变为\(k\)的倍数,前\(2k\)次操作,可以使得那些只出现了两次\(x_i\)的全部与之对应的\(a_i\)变为\(k\)的倍数...
预处理出每个数\(a_i\)需要的\(x_i\),当\(x_i\)相同的个数为\(cnt_{x_i}\)时,需要执行到\(x_i+(cnt_{x_i}-1)k\)次...
所以答案就是,维护出最大的\((cnt_{x_i}-1)\),表示要执行多少次\(k\),再维护出最大的\(x_i\),需要再执行\(x_i\)次,最后再加\(1\)即可(\(0\)开始的第一步)。
ps:个人写的比较复杂,仅供参考。
Code:
ll q[N];
map<ll,ll>mp;
int main(){
int t;
t=read();
while(t--){
ll n;
n=read();
ll k;
k=read();
ll maxxy=-3e18;//最大xi
ll maxxz=-3e18;//执行k多少次
int f=0;
rep(i,1,n){
q[i]=read();
ll y=((k-q[i])%k+k)%k;//xi
//debug(y);
if(y==0){
continue;
}
f=1;
if(mp[y]==0){
mp[y]++;
}
else{
mp[y+k*mp[y]]=mp[y]+1;
mp[y]++;
}
}
if(f==0){
puts("0");
mp.clear();
continue;
}
for(auto it : mp){
maxxz=max(maxxz,it.second);
}
for(auto it : mp){
if(it.second==maxxz)
maxxy=max(maxxy,((it.first)%k));//最大的xi
}
printf("%lld\n",(maxxz-1ll)*k+maxxy+1ll);
mp.clear();
}
return 0;
}
E1. Reading Books (easy version)
题意:
给定\(n\)本书,每本书有\(3\)个属性,分别表示\(t,a,b\),读这本数需要的时间,是否被Alice喜欢,是否被Bob喜欢。
两人必须一起读书,必须同时有书在手上。找出一种方案,使得Alice和Bob都至少读\(k\)本书,问最少的读书时间。
思路:
这题偏向理解,赛时没注意到不会出现一人没书读的情况,疯狂WA5,最后自闭。
- 首先对于两者都不喜欢的书,可以直接忽略。
- 考虑贪心,将其他三种书分类排序(从小到大)。
- 两者都喜欢的书所需最短时间\(≤\)A喜欢的书的最短时间+B喜欢的书的最短时间,选两者都喜欢的最少时间的。
- 两者都喜欢的书所需最短时间\(>\)A喜欢的书的最短时间+B喜欢的书的最短时间,选一本A喜欢的最少时间的,一本B喜欢的最少时间的。
- A喜欢的书已经取完了或者B喜欢的书已经取完了,则只能选一本两者都喜欢的书。
如果觉得数组模拟困难可以考虑multiset或者优先队列queue维护。
Code:
ll q[N];
ll a[N];
ll b[N];
ll t[N];
multiset<ll>s1,s2,s3;
multiset<ll>::iterator it;
int main(){
int n;
n=read();
int k;
k=read();
int cnt=0;
rep(i,1,n){
scanll3(t[i],a[i],b[i]);
if(a[i]==0&&b[i]==1){
s3.insert(t[i]);
cnt++;
}
if(a[i]==1&&b[i]==1){
s2.insert(t[i]);
cnt+=2;
}
if(a[i]==1&&b[i]==0){
s1.insert(t[i]);
cnt++;
}
}
int con=0;
ll ans=0;
int cnt1,cnt2;
cnt1=cnt2=0;
while(1){
long long it1,it2,it3;
it1=it2=it3=0;
if(s1.size()!=0)it1=*(s1.begin());
if(s2.size()!=0)it2=*(s2.begin());
if(s3.size()!=0)it3=*(s3.begin());
if(cnt1>=k&&cnt2>=k)break;
if(s1.size()==0&&s2.size()==0&&s3.size()==0)break;
if((it1==0||it3==0)&&it2==0)break;
if(it1+it3<it2&&it1!=0&&it2!=0&&it3!=0){
if(it1!=0)cnt1++;
if(it3!=0)cnt2++;
ans+=it1+it3;
it=s1.find(it1);
s1.erase(it);
it=s3.find(it3);
s3.erase(it);
}
else if(it1+it3>=it2&&it1!=0&&it2!=0&&it3!=0){
cnt1++;
cnt2++;
ans+=it2;
it=s2.find(it2);
s2.erase(it);
}
else if(it1==0&&it3==0&&it2!=0){
cnt1++;
cnt2++;
ans+=it2;
it=s2.find(it2);
s2.erase(it);
}
else if(it1!=0&&it3!=0&&it2==0){
cnt1++;
cnt2++;
ans+=it1+it3;
it=s1.find(it1);
s1.erase(it);
it=s3.find(it3);
s3.erase(it);
}
else if(it1!=0&&it3==0&&it2!=0){
cnt1++;
cnt2++;
ans+=it2;
it=s2.find(it2);
s2.erase(it);
}
else if(it1==0&&it3!=0&&it2!=0){
cnt1++;
cnt2++;
ans+=it2;
it=s2.find(it2);
s2.erase(it);
}
//debug(ans);
}
if(cnt1<k||cnt2<k){
cout<<"-1";
return 0;
}
cout<<ans;
return 0;
}
小坑点:
值得一提的是,multiset中的erase(x),如果x是元素值,会将多个x全部删去,而不是只删去一个。正确的做法,是先find(x)找到迭代器再erase即可。
此题使用优先队列会更为方便。如果用multiset必须注意如上小坑点,否则你会因此不断WA5...