A. Required Remainder
题意:
T
T
T组询问,给定
x
,
y
,
n
(
2
≤
x
≤
1
0
9
;
0
≤
y
<
x
;
y
≤
n
≤
1
0
9
)
x,y,n (2≤x≤10^9; 0≤y<x; y≤n≤10^9)
x,y,n(2≤x≤109;0≤y<x;y≤n≤109),求最大的整数
k
k
k,满足
0
≤
k
≤
n
且
k
m
o
d
x
=
y
0≤k≤n且k mod x=y
0≤k≤n且kmodx=y
思路:
转换公式
k
=
k
1
∗
x
+
y
,
k
1
∈
R
k=k1*x+y,k1∈R
k=k1∗x+y,k1∈R ,则需要找到最大的
k
1
k1
k1,使得
k
1
∗
x
+
y
<
=
n
k1*x+y<=n
k1∗x+y<=n,则
k
1
m
a
x
=
(
n
−
y
)
/
x
k1_{max}=(n-y)/x
k1max=(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
T
T组询问,给定一个
n
,
1
≤
n
≤
1
0
9
n,1≤n≤10^9
n,1≤n≤109,每次操作将
n
n
n乘
6
6
6(能被
6
6
6整除),或者将
n
n
n乘
2
2
2。问最少多少次操作可以将
n
n
n变换乘
1
1
1。否则输出
−
1
-1
−1。
思路:
暴力+贪心,当前能除
6
6
6就除,否则乘2,
−
1
-1
−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){
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
T
T组询问,每组给定一个长度为
n
n
n的括号字符串,一个括号字符串合法定义如下:
"
(
)
"
"()"
"()" is regular bracket sequence;if
s
s
s regular bracket sequence then
"
(
"
+
s
+
"
)
"
"(" + s+")"
"("+s+")" is regular bracket sequence;if
s
s
s and
t
t
t are regular bracket sequences then
s
s
s +
t
t
t is regular bracket sequence.
每次移动可以将一个括号移动至头部或者尾部,问最少多少次移动使得给定字符串合法。输入满足,
n
n
n是偶数,有
n
2
\frac{n}{2}
2n个
)
)
),
n
2
\frac{n}{2}
2n个
(
(
(。
思路:
先做括号匹配,考虑需要移动的情况,需要移动的字符串一定是
)
)
)
.
.
.
(
(
(
)))...(((
)))...(((的形式,那么做法就很显然了,正常做括号匹配,需要移动的次数就是最后栈大小的一半。
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
n
n的数组
a
a
a,起初,有一个整数
x
=
0
x=0
x=0,每一次操作可以选择将数组中的一个数
a
i
,
a
i
=
a
i
+
x
a_i,a_i=ai+x
ai,ai=ai+x,随后
x
=
x
+
1
x=x+1
x=x+1,或者只执行
x
=
x
+
1
x=x+1
x=x+1,问需要最少多少次操作使数组
a
a
a中的数都能整除
k
k
k。
思路:
可以看出,每个数
a
i
a_i
ai需要加上某个
x
i
x_i
xi,才能被
k
k
k整除。前
k
k
k次操作,可以使得那些只出现了一次
x
i
x_i
xi的全部与之对应的
a
i
a_i
ai变为
k
k
k的倍数,前
2
k
2k
2k次操作,可以使得那些只出现了两次
x
i
x_i
xi的全部与之对应的
a
i
a_i
ai变为
k
k
k的倍数…
预处理出每个数
a
i
a_i
ai需要的
x
i
x_i
xi,当
x
i
x_i
xi相同的个数为
c
n
t
x
i
cnt_{x_i}
cntxi时,需要执行到
x
i
+
(
c
n
t
x
i
−
1
)
k
x_i+(cnt_{x_i}-1)k
xi+(cntxi−1)k次… 所以答案就是,维护出最大的
(
c
n
t
x
i
−
1
)
(cnt_{x_i}-1)
(cntxi−1),表示要执行多少次
k
k
k,再维护出最大的
x
i
x_i
xi,需要再执行
x
i
x_i
xi次,最后再加
1
1
1即可(
0
0
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;
ll maxxz
=-3e18;
int f
=0;
rep(i
,1,n
){
q
[i
]=read();
ll y
=((k
-q
[i
])%k
+k
)%k
;
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
));
}
printf("%lld\n",(maxxz
-1ll)*k
+maxxy
+1ll);
mp
.clear();
}
return 0;
}
E1. Reading Books (easy version)
题意:
给定
n
n
n本书,每本书有
3
3
3个属性,分别表示
t
,
a
,
b
t,a,b
t,a,b,读这本数需要的时间,是否被Alice喜欢,是否被Bob喜欢。 两人必须一起读书,必须同时有书在手上。找出一种方案,使得Alice和Bob都至少读
k
k
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
);
}
}
if(cnt1
<k
||cnt2
<k
){
cout
<<"-1";
return 0;
}
cout
<<ans
;
return 0;
}
小坑点:
值得一提的是,multiset中的erase(x),如果x是元素值,会将多个x全部删去,而不是只删去一个。正确的做法,是先find(x)找到迭代器再erase即可。 此题使用优先队列会更为方便。如果用multiset必须注意如上小坑点,否则你会因此不断WA5…
其他题待补