https://codeforces.com/contest/1556
A题
给你两个数初始值为 0 0 0,可以同时加上一个数或者一个加一个减,问最少需要多少次能够达到目标值
- 显然应该先同时加上两数平均数,然后再一个加一个减,特判相等、无解的情况即可
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 100;
const double eps = 1e-6;
int main(){
#ifdef LOCAL
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
int t, c, d;
cin >> t;
while(t--){
cin >> c >> d;
if(c > d) swap(c, d);
int k = d - c;
if(k == 0 && c == 0) cout << 0 << '\n';
else if(k == 0) cout << 1 << '\n';
else if(k & 1) cout << -1 << '\n';
else{
cout << 2 << '\n';
}
}
return 0;
}
B题
给出一个数组,每次只能交换相邻的两个数,问最少多少步让这个数组相邻的数字奇偶性不同
- 如果数组元素个数是偶数,奇数元素和偶数元素数量必须相等,此时有两种情况,分别是奇数在奇数位和奇数在偶数位;如果数组元素是奇数,奇数元素和偶数元素数量差 1 1 1,多的那个在奇数位
- 思路就是把该放到对应位置的数字放好,也就是第一个奇数放到 1 1 1,第二个奇数放到 3 3 3,以此类推,这样做为什么是对的呢?我们考虑最后的奇数,为了用最少的步骤,他肯定要去最后面的位置,以此类推
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 100;
const double eps = 1e-6;
int a[MAXN];
int main(){
#ifdef LOCAL
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
int t, n;
cin >> t;
while(t--){
cin >> n;
int odd, even;
int ans = 0;
odd = even = 0;
int l = 1;
for(int i=1;i<=n;i++){
cin >> a[i];
if(a[i] & 1) odd++;
else even++;
}
if(n & 1){
if(abs(odd - even) != 1){
cout << -1 << '\n';
continue;
}
if(odd > even){
for(int i=1;i<=n;i++){
if(a[i] & 1){
ans += abs(i - l);
l += 2;
}
}
}else{
for(int i=1;i<=n;i++){
if(!(a[i] & 1)){
ans += abs(i - l);
l += 2;
}
}
}
}else{
if(odd != even){
cout << -1 << '\n';
continue;
}else{
for(int i=1;i<=n;i++){
if(a[i] & 1){
ans += abs(i - l);
l += 2;
}
}
int tmp = ans;
ans = 0;
l = 1;
for(int i=1;i<=n;i++){
if(!(a[i] & 1)){
ans += abs(i - l);
l += 2;
}
}
ans = min(ans, tmp);
}
}
cout << ans << '\n';
}
return 0;
}
C题
- 这个题 O ( n ) O(n) O(n)算法可以用栈来做,具体思路是可以设一个 s t a c k < p a i r < l l , l l > > stack<pair<ll,ll>> stack<pair<ll,ll>>,这样 p a i r pair pair的 f i r s t first first表示未匹配的左括号数量, s e c o n d second second表示并列的括号数,写出一堆问题…
- 不过题目并没有那样出,数据仅仅到了 1 e 3 1e3 1e3,也就是可以使用 n 2 n^2 n2的算法,那么我们可以枚举每一组左括号,看这组左括号能够匹配多少个右括号,这样就省下很多麻烦,思路也变得清晰
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdio>
#include <map>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 100;
const int INF = 0x3f3f3f3f;
ll a[MAXN];
int main(){
#ifdef LOCAL
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
ios::sync_with_stdio(false);
int n;
cin >> n;
ll ans = 0;
for(int i=1;i<=n;i++) cin >> a[i];
for(int i=2;i<=n;i+=2){
ll now;//当前左括号
ll now_r;//当前之后的左括号
now_r = 0;
if(a[i] > a[i - 1]){
ans += a[i - 1];
continue;
}else{
now = a[i - 1] - a[i];
ans += a[i];
}
for(int j=i+2;j<=n;j+=2){
if(a[j] < a[j - 1]){
now_r += a[j - 1] - a[j];
}else{
ll rest_r = a[j] - a[j - 1];
if(rest_r < now_r){
now_r -= rest_r;
}else{
rest_r -= now_r;
now_r = 0;
if(now < rest_r){
ans += now + 1;
break;
}else{
now -= rest_r;
ans += rest_r + 1;
}
}
}
}
}
cout << ans;
return 0;
}
- 备注:这题以后还得看看(题解公式不懂)
https://codeforces.com/blog/entry/94384
补充:从一大佬那里学来一个递归写法如下,思路非常清晰
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdio>
#include <map>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 100;
const int INF = 0x3f3f3f3f;
ll ans;
stack<pair<ll, ll> > st;
void solve(int l, int r, ll u){
if(l > u){
ans += u;
st.push({l - u, 1});
}else if(l == u){
ans += u;
l = st.top().first;
r = st.top().second;
st.pop();
ans += r;
st.push({l, r + 1});
}else{
ans += l;
u -= l;
l = st.top().first;
r = st.top().second;
st.pop();
ans += r;
if(l) solve(l, r, u);
else st.push({0, 0});
}
}
void solve(){
int n;
ll u;
cin >> n;
st.push({0, 0});
for(int i=1;i<=n;i++){
cin >> u;
if(i & 1){
st.push({u, 0});
}else{
int l = st.top().first;
int r = st.top().second;
st.pop();
if(l) solve(l, r, u);
else st.push({0, 0});
}
}
cout << ans;
}
int main(){
#ifdef LOCAL
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
ios::sync_with_stdio(false);
solve();
return 0;
}
D题
交互题。已知数组长度 n n n,可以进行最多 2 n 2n 2n次询问,每次询问数组任意两个数之间的 o r or or或者 a n d and and,现在要求第 k k k小
- 这个题目的关键点在于我们要知道 a + b = ( a ∣ b ) + ( a & b ) a+b=(a|b)+(a\&b) a+b=(a∣b)+(a&b),其证明可以通过讨论四种情况来进行判断,那么一旦知道了这个结论,那么思路就不难想到,我们要对前三个数字两两进行询问它们之间的与和或,这样可得到 a 1 + a 2 , a 1 + a 3 , a 2 + a 3 a_1+a_2,a_1+a_3,a_2+a_3 a1+a2,a1+a3,a2+a3的值,那么就可以把数组的前三个元素求出来,这样往后递推询问即可得到全部数组
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdio>
#include <map>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 100;
const int INF = 0x3f3f3f3f;
int a[MAXN];
int Get_or(int i, int j){
int x;
cout << "or " << i << ' ' << j << endl;
cin >> x;
return x;
}
int Get_and(int i, int j){
int x;
cout << "and " << i << ' ' << j << endl;
cin >> x;
return x;
}
void Get_Finish(int k){
cout << "finish " << a[k];
}
int main(){
#ifdef LOCAL
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
ios::sync_with_stdio(false);
int n, k;
cin >> n >> k;
int x1 = Get_and(1, 2);int x2 = Get_or(1, 2);
int y1 = Get_and(2, 3);int y2 = Get_or(2, 3);
int z1 = Get_and(1, 3);int z2 = Get_or(1, 3);
a[1] = (x1 + x2 + z1 + z2 - y1 - y2) / 2;
a[2] = (y1 + y2 - z1 - z2 + x1 + x2) / 2;
a[3] = (y1 + y2 + z1 + z2 - x1 - x2) / 2;
for(int i=3;i<n;i++){
x1 = Get_and(i, i + 1);
x2 = Get_or(i, i + 1);
a[i + 1] = x1 + x2 - a[i];
}
sort(a + 1, a + 1 + n);
Get_Finish(k);
return 0;
}