Codeforces Round #653(Div3)
A. Required Remainder
A. Required Remainder
题意:给出 x , y , n x, y, n x,y,n,求一个最大的 k ∈ [ 1 , n ] k\in[1, n] k∈[1,n]使 k % x = y k\%x = y k%x=y。
思路:已知 k = u ∗ x + y k = u*x + y k=u∗x+y, u u u为未知。我们是在区间 [ 1 , n ] [1, n] [1,n]之间取 k k k,我们可以得到在区间 [ 1 , n ] [1, n] [1,n]中 x x x的最大倍数 n / x ∗ x ^n/_x*x n/x∗x,如果 n / x ∗ x + y > n ^n/_x*x+y > n n/x∗x+y>n,那 k = n / x ∗ x − x + y k = ^n/_x*x-x+y k=n/x∗x−x+y,如果 n / x ∗ x + y < = n ^n/_x*x+y <= n n/x∗x+y<=n,那 k = n / x ∗ x + y k = ^n/_x*x+y k=n/x∗x+y。
代码:
#include<bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while(t--) {
int x, y, n, m;
cin >> x >> y >> n;
m = n / x * x;//x在区间的最大倍数。
if(m + y > n) cout << m + y - x << endl;
else cout << m + y << endl;
}
return 0;
}
B. Multiply by 2, divide by 6
B. Multiply by 2, divide by 6
题意:给出一个数n,对数有两种操作 n ∗ = 2 n *= 2 n∗=2或 n / = 6 n /= 6 n/=6,问是否可以在若干次操作后使 n = 1 n=1 n=1,如果可以求最小操作次数,如果不行输出-1。
思路:要使 n n n最后变成1,只有在n = 6的时候 n / = 6 n/=6 n/=6才行,所以我们要检验 n = 3 x ∗ 2 y n = 3^x*2^y n=3x∗2y且 x > = y x >= y x>=y,如果 x < y x < y x<y那到最后一定会有2的倍数余下。
代码:
#include<bits/stdc++.h>
using namespace std;
int come(int *m) {
//求3^x,中的x。
int ans = 0;
while(*m) {
if(*m % 3 == 0) ans++;
else break;
*m /= 3;
}
return ans;
}
int check(int n) {
//确定n/(3^x)是2的倍数,并且返回2^y中的y
for(int i=0; i<32; i++) {
if(n == 1<<i) return i;
}
return -1;
}
int main() {
int t;
cin >> t;
while(t--) {
int n, m, x;
cin >> n;
if(n == 1) {
//特判
cout << 0 << endl;
continue;
}
if(n % 3 == 0) {
m = come(&n);
x = check(n);
if(x > m || x == -1) cout << -1 << endl;
else cout << m+(m-x) << endl;
}
else cout << -1 << endl;
}
return 0;
}
C. Move Brackets
C. Move Brackets
题意:给出一串由 ′ ( ′ , ′ ) ′ '(',')' ′(′,′)′组成的字符串,可以将其中任意一个字符移到字符串开头或结尾,求最小移动次数,使每个左括号’(’,可以和右括号’)'匹配:类似于 ( ( ) ) , ( ) ( ) (()),()() (()),()()
思路:每个可以先去掉可以匹配的括号“()”,再将剩下的类似于"))))(((("的字符串再移动,我们只需要将所有右括号移到右边就完成了匹配,也就是字符串长度的一半。
代码:
#include<bits/stdc++.h>
using namespace std;
char s[55];
int main() {
int t;
cin >> t;
while(t--) {
stack<char> S;
int n;
cin >> n >> s;
// cout << n << s;
for(int i=0; s[i]; i++) {
if(S.empty() || S.top() == ')' || (S.top() == '(' && s[i] == '(')) {
S.push(s[i]);
}
else {
S.pop();
}
}
cout << S.size()/2 << endl;
}
return 0;
}
D. Zero Remainder Array
D. Zero Remainder Array
题意:给出一个含 n n n个元素的序列 a a a,和一个数字 k k k。有一个数 x x x,有两种操作:1. a i + x , x + + a_i+x,x++ ai+x,x++,2 . x + + .x++ .x++。问最少操作数使a中的每个元素都是k的倍数。
思路:我们按 k − ( a i % k ) k-(a_i\%k) k−(ai%k)的大小进行排序。小的排在前面,优先加上 x x x使 a i a_i ai成为 k k k的倍数。但是在这种情况下可能会出现有相同的 k − ( a i % k ) k-(a_i\%k) k−(ai%k)出现,所以我们必须将 k − ( a i % k ) k-(a_i\%k) k−(ai%k)加k,使他们不相等且依然是k的倍数。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e5+10;
ll s[N];
map<ll, ll> M;//map标记同一个k-(a_i\%k)出现次数,对应加上多少倍的k。
int main() {
ll t;
cin >> t;
while(t--) {
M.clear();
ll n, k, m, ans = 0, x = 0;
cin >> n >> k;
for(int i=0; i<n; i++) {
scanf("%lld", &s[i]);
m = k - (s[i] % k);
if(m == k) s[i] = 0;
else {
s[i] = m+M[m]*k;//计算k-(a_i\%k)。
M[m]++;//出现次数。
}
}
sort(s, s+n);//排序。
for(int i=0; i<n; i++) {
//计算结果。
if(s[i] != 0) {
ans += s[i] - x + 1;
x = s[i] + 1;
}
}
cout << ans << endl;
}
return 0;
}
一个总结:x的每次操作都会加1,所以最终x的值就是操作次数,我们求出最大的 k − ( a i % k ) k-(a_i\%k) k−(ai%k),x一定会在最后达到这个数字,所以(这个数字+1)就是结果。
优化代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e5+10;
ll s[N];
map<ll, ll> M;//map标记同一个k-(a_i\%k)出现次数,对应加上多少倍的k。
int main() {
ll t;
cin >> t;
while(t--) {
M.clear();
ll n, k, m, ans = 0, x = 0;
cin >> n >> k;
for(int i=0; i<n; i++) {
scanf("%lld", &s[i]);
m = k - (s[i] % k);
if(m == k) s[i] = 0;
else {
s[i] = m+M[m]*k;//计算k-(a_i\%k)。
M[m]++;//出现次数。
}
ans = max(ans, s[i]);
}
ans != 0 ? cout << ans+1 << endl : cout << 0 << endl;
}
return 0;
}