Educational Codeforces Round 102

一. A.Replacing Elements

A题链接

(1)题意:

给你一个数组,可以选择其中三个不同位置的数i,j,k,使得a[i] = a[j] + a[k],问能不能经过0次或者多次操作使得数组里面的数都小于等于d

(2)题解:

能够使数组在任何时候都小于等于d的话,要么是存在两个数相加都不超过d,就可以使得任何一个数都 = 这两个数相加,要么就是所有数都已经不超过d,直接满足条件

(3)代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

int t,a[1000];

void solve(){
	int n,d,flag = 0;
	cin>>n>>d;
	for(int i = 1; i <= n; i++){
		cin>>a[i];
		if(a[i] > d)	flag = 1;
	}
	if(!flag)	{
		puts("YES");
		return ;
	}
	sort(a+1,a+1+n);
	if(a[1] + a[2] <= d)    puts("YES");
	else
	    puts("NO");
}

int main(){
	cin>>t;
	while(t--)
		solve();
	return 0;
} 

二. B.String LCM

B题链接

(1)题意:

给你两个只由a,b构成的字符串,求出他们的最小倍串,例如aa,aaa的最小倍串就是aaaaaa

(2)题解:

我们可以根据给出的两个字符串来凑出最小倍串,因为是ab*2的规则是 = abab,所以当出现该构造最小倍串的位置上的两个字符串的字符不一致时,就说明凑不出来。比如说s[i] != t[i] 时,就说明不存在最小倍串,且这构造过程最多进行s.size() * t.size() / gcd(s,t)次操作。

(3)代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

int t;

void solve(){
	string s,t,ans;
	cin>>s>>t;
	int l1 = 0,l2 = 0;
	for(int i = 1; i <= 400; i++){	//字符串的长度不超过20,为了图方便就直接写400
		if(s[l1] != t[l2]){
			puts("-1");
			return ;
		}
		ans += s[l1];
		l1 = (l1+1)%s.size();
		l2 = (l2+1)%t.size();
		if(l1 == 0 && l2 == 0){
			cout<<ans<<endl;
			return ;
		}
	}
}

int main(){
	cin>>t;
	while(t--)
		solve();
	return 0;
} 

三. C. No More Inversions

C题链接

(1)题意:

给你一个a数组:1,2,3…k,k-1…k - (n-k),当i < j 且 a[i] > a[j] 时就是一个逆序对
设定b[i] = p[a[i]],p数组内的元素为1,2,3… k,可以任意排列p数组内的元素,求出一个排列使得b数组的字典序最大且b数组中的逆序对总数不超过a数组中的逆序对总数

(2)题解:

当你多写几组数据之后,例如(5,3),(6,4),(8,5)就会得到他们的共同点
a数组中的逆序总数是固定的——(n-k) * (n-k)个,而且出现两次的元素会呈现中心对称,那么b数组中也是同样的情况,把p数组中最大的元素向前移动n-k位,在b数组中会关于第k位元素对称,最大值与其他值会产生(n-k) * 2-1个逆序对,这时有n-k-1个数也会产生的x个逆序对就恰好 = (n-k) * (n-k),不管如何在p数组中排列这剩下n - k - 1个数,都会中心对称,产生同样数量的逆序对。
所以n-k就代表最大元素前移多少位,前1 ~ 2k-n-1个数升序排列,后2k-n ~ k个数降序排列。

(3)代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

int t;

void solve(){
	int n,k;
	cin>>n>>k;
	int p = n - k;
	for(int i = 1; i < k - p; i++)
		cout<<i<<" ";
	for(int i = p; i >= 0; i--){
		cout<<k<<" ";
		k--;
	}
	cout<<endl;
}

int main(){
	cin>>t;
	while(t--)
		solve();
	return 0;
} 

四. D. Program

D题链接

(1)题意:

‘-’代表减1,‘+’代表加1,给定一串加减号,有m次查询,每次查询给定一个区间,问去掉这个区间后,剩下的加减号在不改变原次序的情况下会产生多少个不同的数,初始化为0

(2)题解:

可以根据+1,-1来做前缀和,然后利用树状数组来做区间查询,因为每次给定的被减区间将整个区间划分成了两半,所以其结果为max(前段的最大值,后段的最大值) - min(前段的最小值,后段的最小值) + 1。
注意每次最开始初始化了x的值为0,所以求前段最大值(/最小值)时都要和0在做一次比较,然后后段的最大值(/最小值)要减去给定被减区间的值

(3)代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

int t,n,m,b[200005],c[200005],sum[200005];

int lowbit(int x){
	return x & (-x);
}

void build1(int id,int val){
	b[id] = val;
	for(int i = 1; i < lowbit(id); i++){
		b[id] = max(b[id],b[id-i]);
	}
}

int query1(int l,int r){
	int ans = sum[r];
	while(r >= l){
		ans = max(sum[r],ans);
		r--;
		for(;r - l >= lowbit(r); r -= lowbit(r))
			ans = max(b[r],ans);
	}
	//cout<<"ans = "<<ans<<endl;
	return ans;
}

void build2(int id,int val){
	c[id] = val;
	for(int i = 1; i < lowbit(id); i++){
		c[id] = min(c[id],c[id-i]);
	}
}

int query2(int l,int r){
	int ans = sum[r];
	while(r >= l){
		ans = min(sum[r],ans);
		r--;
		for(;r - l >= lowbit(r); r -= lowbit(r))
			ans = min(c[r],ans);
	}
	return ans;
}

void solve(){
	cin>>n>>m;
	memset(b,0,sizeof(b));
	memset(c,0,sizeof(c));
	memset(sum,0,sizeof(sum));
	for(int i = 1; i <= n; i++){
		char x;
		int a;
		cin>>x;
		a = (x == '+'? 1 : -1);
		sum[i] = sum[i-1] + a;
		build1(i,sum[i]);
		build2(i,sum[i]);
	}
	for(int i = 1; i <= m; i++){
		int l,r;
		cin>>l>>r;
		int x = sum[r] - sum[l-1];
		int max1 = max(max(0,query1(1,l-1)),query1(r+1,n) - x);
		int min1 = min(min(0,query2(1,l-1)),query2(r+1,n) - x);
		cout<<max1 - min1 + 1<<endl;
	}
}

int main(){
	cin>>t;
	while(t--)
		solve();
	return 0;
}