Deltix Round, Summer 2021 (Div. 1 + Div. 2)

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.

  1. x % 2 != 0,无解
  2. x = 0
    1. a = 0, 0
    2. a != 0 ,1
  3. 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)绑在一起处理,枚举起点,再枚举终点。

枚举的左右端点判断的是在枚举的区间,能与当前起点左括号任意一个为起点匹配的个数。

往右枚举过程中,可能会出现一对左右数量不会相等的

  1. 若左括号多,那么记录一下数量,之后要想与起点左括号匹配,则必须先把这些括号匹配完,
  2. 若右括号大于左括号,则多的部分先拿去抵消之前留下的非起点的左括号,然后还有剩余则拿去和起点剩下的左括号匹配
#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} sumrsuml1==0&&min{suml...sumr}>=suml1

最终答案为: 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值