Deltix Round, Summer 2021 (Div. 1 + Div. 2)
A
解释:
令a < b , 操作一步可以构造 ( − x , x ) (-x,x) (−x,x) 再次操作可以使得使得a和b补全a和b的2x差值,当x为奇数,无解。
特判,当x为0且a=0,则操作0步,当x为0且a != 0,操作一步。
综上,令 x = b - a.
- x % 2 != 0,无解
- x = 0
- a = 0, 0
- a != 0 ,1
- x % 2 = 0 && x != 0, 2
代码
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
using namespace std;
const int N = 2e2 + 10;
#define int long long
typedef long long ll;
signed main(){
IOS
int tt; cin>>tt;
while(tt --){
int a,b; cin>>a>>b;
int t = abs(a-b);
if(t % 2) cout<<-1<<endl;
else{
if(t == 0){
if(a == 0) cout<<0<<endl;
else cout<<1<<endl;
}else cout<<2<<endl;
}
}
return 0;
}
B
解释
合法状态一定是奇数偶数隔着放,不妨放先把所有奇数放好,那么偶数也相当于放好。
处理出所有奇数的位置,当出现的个数与偶数相等,或者比偶数多一个,少一个,都有解。
对于各种情况,放置位置的情况一定是(1,3,5,7,9,…),(2,4,6,8,10,…)
求出最小的值就行。
代码
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
using namespace std;
const int N = 2e2 + 10;
#define int long long
typedef long long ll;
signed main(){
IOS
int tt; cin>>tt;
while(tt --){
int n; cin>>n;
vector<int> ve;
for(int i=1;i<=n;i++){
int a; cin>>a;
if(a % 2) ve.push_back(i);
}
if((n%2 == 0 && (int)ve.size() == n/2) || (n%2 == 1 && ((int)ve.size() == n/2 || (int)ve.size() == n/2+1))){
int ans = 1e18;
if(n%2 == 0){
int res = 0;
for(int i=0;i<n/2;i++) res += abs(ve[i]-i*2-1);
ans = min(ans,res);
res = 0;
for(int i=0;i<n/2;i++) res += abs(ve[i]-i*2-2);
ans = min(ans,res);
}else if(n%2 && (int)ve.size() == n/2){
int res = 0;
for(int i=0;i<n/2;i++) res += abs(ve[i]-i*2-2);
ans = min(ans,res);
}else{
int res = 0;
for(int i=0;i<n/2+1;i++) res += abs(ve[i]-i*2-1);
ans = min(ans,res);
}
cout<<ans<<endl;
}else{
cout<<-1<<endl;
}
}
return 0;
}
C
解释
将(1,2),(3,4)… (n-1,n)绑在一起处理,枚举起点,再枚举终点。
枚举的左右端点判断的是在枚举的区间,能与当前起点左括号任意一个为起点匹配的个数。
往右枚举过程中,可能会出现一对左右数量不会相等的
- 若左括号多,那么记录一下数量,之后要想与起点左括号匹配,则必须先把这些括号匹配完,
- 若右括号大于左括号,则多的部分先拿去抵消之前留下的非起点的左括号,然后还有剩余则拿去和起点剩下的左括号匹配
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1e3+10;
const int mod = 1e9+7;
#define int long long
int a[N];
signed main(){
int n; cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
int ans = 0;
for(int i=1;i<=n;i+=2) {
int num1 = a[i]-a[i+1],num2 = 0;
ans += min(a[i],a[i+1]);
for(int j=i+3;j<=n;j+=2) {
if(num1 < 0) break;
if(a[j-1] > a[j]) {
num2 += a[j-1] - a[j];
continue;
}
int t = a[j] - a[j-1];
if(t < num2) num2 -= t;
else if(t == num2) {
num2 = 0;
ans ++;
}else {
t -= num2;
num2 = 0;
ans += min(num1,t) + 1;
num1 -= t;
}
}
}
cout<<ans<<endl;
return 0;
}
D
解释
a + b = a | b + a & b, 先求出前三个的加法。
代码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1e4+10;
const int mod = 1e9+7;
#define int long long
int a[N];
int query(int i,int j) {
cout<<"or "<<i<<" "<<j<<endl;
int a; cin>>a;
cout<<"and "<<i<<" "<<j<<endl;
int b; cin>>b;
return a+b;
}
signed main(){
int n,k; cin>>n>>k;
int q1 = query(1,2);
int q2 = query(2,3);
int q3 = query(1,3);
a[1] = (q1 - q2 + q3) / 2;
a[2] = q1 - a[1];
a[3] = q2 - a[2];
for(int i=4;i<=n;i++) {
int t = query(i-1,i);
a[i] = t - a[i-1];
}
sort(a+1,a+n+1);
cout<<"finish "<<a[k]<<endl;
return 0;
}
E
解释
维护差值序列 c ( i ) = b ( i ) − a ( i ) c(i) = b(i) - a(i) c(i)=b(i)−a(i), 对于每一个不同奇偶位置,一个+,一个-,可以看成括号序列的匹配问题。大于0的为左括号,小于0的为右括号。
维护前缀和 s u m n sum_n sumn,当能变成相等当且仅当 ( l , r ) (l,r) (l,r)是一个合法的括号序列。
则必须满足: s u m r − s u m l − 1 = = 0 & & m i n { s u m l . . . s u m r } > = s u m l − 1 sum_r - sum_{l-1} == 0 \&\& min\{sum_l...sum_r\} >= sum_{l-1} sumr−suml−1==0&&min{suml...sumr}>=suml−1
最终答案为: m a x { s u m l . . . s u m r } max\{sum_l...sum_r\} max{suml...sumr}
因为每次会去除类似 ()()()()…()的子序列,两个被分开的左括号能同时被选,当相隔它们的右括号被匹配完时,则不能同时被选。
然后用ST表或者线段树求区间最值。
代码
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
#define int long long
typedef long long ll;
int a[N],Log[N];
int mx[N][20],mn[N][20];
void init() {
for(int i=1;(1<<i)<N;i++) Log[1<<i] ++;
for(int i=1;i<N;i++) Log[i] += Log[i-1];
}
void ST(int n) {
for(int i=1;i<=n;i++)
for(int j=0;j<20;j++)
mx[i][j] = 0,mn[i][j] = 1e18;
for(int i=1;i<=n;i++) mx[i][0] = mn[i][0] = a[i];
for(int j=1;j<=Log[n];j++)
for(int i=1;i<=n-(1 << j)+1;i++){
mn[i][j] = min(mn[i][j-1],mn[i+(1<<(j-1))][j-1]);
mx[i][j] = max(mx[i][j-1],mx[i+(1<<(j-1))][j-1]);
}
}
int query_max(int l,int r) {
int k = Log[r - l + 1];
return max(mx[l][k],mx[r-(1<<k)+1][k]);
}
int query_min(int l,int r) {
int k = Log[r - l + 1];
return min(mn[l][k],mn[r-(1<<k)+1][k]);
}
signed main(){
IOS
init();
int n,q; cin>>n>>q;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
int b; cin>>b;
a[i] = b - a[i];
a[i] += a[i-1];
}
ST(n);
while(q --){
int l,r; cin>>l>>r;
if(a[l-1] != a[r] || query_min(l,r) < a[l-1]) cout<<"-1\n";
else cout<<(query_max(l,r) - a[l-1])<<"\n";
}
return 0;
}