Codeforces Round#768(Div.2)A-F

A. Min Max Swap

题意

给定两个数组a、b,可以任意的交换ai 、bi。使得max(a1,a2,…,an)⋅max(b1,b2,…,bn) 的值最小

题解:

1.从全局来考虑,由于只是改变次序,并不会改变值得大小,最终答案是a数组最大值乘上b数组最大值,设ans=m1*m2.那么乘积中的一个数一定是两个数组中的最大值。即m1=max(a1,a2,…,an,b1,b2,…,bn).
2.固定m1后,让ans最小只需要让m2最小,那么我们可以把较大的值都放在一个数组里,把较小的数放在一个数组里即可得到最终的答案。

代码:

#include<bits/stdc++.h>
using namespace std;
int n;
int a[1005],b[1005];
void solve()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	for (int i = 1; i <= n; i++)cin >> b[i];
	for (int i = 1; i <= n; i++)
	{
		if (a[i] >= b[i])swap(a[i], b[i]);
	}
	int maxa = -1, maxb = -1;
	for (int i = 1; i <= n; i++)
	{
		maxa = max(a[i], maxa);
		maxb = max(b[i], maxb);
	}
	cout << maxa * maxb << endl;
}
int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		solve();
	}
}

B. Fun with Even Subarrays

题意

给定一个数组,每次可以选择两个值分别作为起点和复制长度,l,k。For 例如a=[2,1,3,4,5,3],选择l=1,k=2,然后a[1]=a[3],a[2]=a[4],a=[3,4,3,4,5,3].问最少需要几步可以使得a中所有数字都相同

题解

1.尝试思考最终的数组是任意的吗?还是有个固定的值?
我们可以发现最后一个值是没法动的,所以要让数组变成所有值都一样那么最终数组的值是一定的,就是最后一个值a[n]。
2.怎么样让步数最少?
因为选择l、k是没有额外花费的,所以尽量选择较长的一段相同的a[n],赋值到前面去,所以很容易逆向思考,先将后面的尾巴染成a[n],遇到不是a[n]时就将后面所有的直接把前面刷成a[n]即可,刷的长度是现在已经染完的长度,所以。所以维护一下当前已经染过的长度,每次遇到不同的值就要开染。

#include<bits/stdc++.h>
using namespace std;
int n;
int a[200005],b[1005];
void solve()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	int dif = 0;
	for (int i = 1; i <= n; i++)
		if (a[i] != a[n])dif++;
	int cplen = 0,cnt=0;
	for (int i = n; i >= 1;)
	{     
		//cout << i << " " << cplen << " " << cnt << endl;
		int tid = i,tlen=cplen;
		if (a[i] == a[n])cplen++,i--;
		else
		{   
			cnt++;
			for (int k = 0; k < cplen; k++)
			{   
				if (a[tid - k] != a[n])dif--;
				if (dif <= 0)break;
		    }
			cplen *= 2;
			if (dif <= 0)break;
			i -= cplen / 2;
		}
		
	}
	cout << cnt << endl;
	   

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

C. And Matching

题意:

给定一个值n,表示数组为[0-n-1],其中n一定是2的整数幂,将数字两两配对分别为a[i],b[i],使得 ∑ a i & b i = k \sum a_{i} \&b_{i}=k ai&bi=k,

题解:

1.直接构造出一个答案为k,使得剩下的组合都为0即可。
我们知道:

  1. n-1&k=k
  2. 设c(k)为k的补码, k&c(k)=0

再分情况讨论:
Case 0<=k<n−1:
在这种情况下,可以将除 0、k、c(k) 和 n-1 之外的每个元素与其补码配对,然后将 0 与 c(k) 和 k 与 n-1 配对,0&c(k)=0 和k&(n-1)=k。
Case k=n−1:
n=4 则没有解决方案,如果 n≥8 则可以通过以下方式构造答案:
(n−1)&(n−2)=n−2,
(n−3)&1=1,
0&2=0
剩下的数则按照补码来组合x&c(x)=0
(n−2)+1+0+0+…+0=n−1.

#include<bits/stdc++.h>
using namespace std;
int n, t, k, x[1000005];
int main() {
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d%d", &n, &k);
		for (int i = 0; i < n; i++) x[i] = i;
		if (k != n - 1) swap(x[k], x[0]);
		else swap(x[n - 2], x[0]), swap(x[1], x[n - 4]);
		if (n == 4 && k == 3) printf("-1\n");
		else
		{
			for (int i = 0; i < n / 2; i++) printf("%d %d\n", x[i], x[n - i- 1]);
		}
	}
}

D. Range and Partition

题意

给定一个数组,将这个数组分为k块,每一块要求在区间[x,y]内的数都要多于在区间外的数。求一个[x,y]使得y-x值最小,并输出k个区间的左右端点。

题解

我们先分析一下题意,找到我们需要解决的问题,以及问题中相互牵制的条件。
寻找到一个范围[x,y],当范围大时显然我们可以任意分割数组,只要满足有k个区间即可。但是题目要求区间范围最小,范围小的话我们就不一定能满足分割出k个区间来
提示1:
专注于如何解决固定区间 [x,y] 的问题。
提示2:
将区间内的数字视为 +1,将其他数字视为 -1
提示3:
尝试将分区关联到具有递增序列的前缀和数组的有效子数组。
首先假设我们已经拥有了一个区间范围[x,y],我们另设一个数组b,当x<=a[i]<=y,即a[i]在范围内时,b[i]=1反之b[i]等于-1.这样一个满足条件的区间,区间和大于0。
问题转换为,找到k个区间和大于等于1的区间。
接下来我们尝试解决这个问题,从全局考虑,要满足有k个子区间大于等于1,那总区间和一定需要大于k。
在这里插入图片描述
让我们对这个式子进行变形,先让每个b[i]都为-1,然后满足条件的a[i],b[i]值变为2,将式子化为
在这里插入图片描述
将式子常数展开,左边的-1求和可以放到右边然后整除。

图3
这样这个式子的含义就变成了,整个数组中在范围[x,y]中的数的个数大于[k+n/2]上取整这么多即可。也就是说只要我们将分成k个区间这个条件简化为了最后这个式子。接下来我们只需要让范围差最小。
解决范围最小问题
既然是整个数组的问题,我们可以将原数组进行升序排列设为A,只需要满足下列式子:
在这里插入图片描述
循环遍历长度为(k+n+1)/2的区间保证满足可以分为k个区间的条件
分为k个区间
双指针输出,只要满足子区间和大于等于1就是一个答案。找到k-1个区间后输出最后一个区间

#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 2e5 + 5;
int t, n, k;
int a[MAXN], b[MAXN];
int main() {
	cin >> t;
	while (t--) {
		cin >> n >> k;
		int len = (n + k + 1) / 2;//确定最小长度
		for (int i = 1; i <= n; i++) {
			cin >> a[i];
			b[i] = a[i];
		}
		sort(b + 1, b + n + 1);//排序
		int x = 0, y = 1e9;
		for (int i = len; i <= n; i++)
			if (b[i] - b[i - len + 1] < y - x)//min(y-x)
				x = b[i - len + 1], y = b[i];
		cout << x << " " << y << endl;
		int s = 0, l = 1;
		for (int i = 1; i <= n; i++) {
			if (a[i] >= x && a[i] <= y) s++;
			else s--;
			if (s >= 1 && k > 1) {
				cout << l << " " << i << endl;
				l = i + 1;
				s = 0;
				k--;
			}
		}
		cout << l << " " << n << endl;
	}
	return 0;
}

E. Paint the Middle

题意:

给定编号从 1 到 n的 n 个元素,元素 i 具有值 ai 和颜色 ci,最初,对于所有 i,ci=0。
可以应用以下操作:
选取三个元素i、j、k(1≤i<j<k≤n),使得ci、cj、ck都等于0且ai=ak,则令cj =1。
m a x ∑ c i max\sum ci maxci为多少

题解:

可以将这题看成区间覆盖的问题,只要两头有个相同的数显然可以将中间所有的ci变为1.所以我们只需要预处理所有的数出现最后一次的位置,然后判断当前的i是否在这个以a[i]为左端点的区间内,如果是则答案数+1,反之贪心的选择一个最右的端点;

#include<bits/stdc++.h>
using namespace std;
int a[200005], ed[200005];
int main()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		ed[a[i]] = i;
	}
	int cover = 0,r=0,res=0;
	for (int i = 1; i <= n; i++)
	{
		r = max(r, ed[a[i]]);
		if (cover > i)res++;
		else cover = r;
	}
	cout << res << endl;
}

F. Flipping Range

题意:

给定一个数组a,与一个集合B,每次可以选择集合B中的一个元素作为翻转a中一段的大小。使得 ∑ a i \sum a_{i} ai最大

题解:

如果我们有x、y∈B(假设x>y),等价于拥有一个x-y大小置入B中,方法是乘以从大小为 x-y 的区间的位置开始的大小为 x 的区间,和一个大小为 y 的区间,结束于与区间 x 相同的位置.或者,将一个大小为 x 的区间与大小为 x−y 的区间在相同位置结束的区间与另一个大小为 y 的区间相乘,该区间的起点与大小为 x 的区间相同。
在这里插入图片描述

对于两个元素 x,y∈B(x>y),可以将 x−y 添加到 B,重复这样做可以得到 gcd(x,y).
让 g=gcd(b1,b2,…,bm:bi∈B),通过应用前面的归约 g 是可以得到的最小元素,所有其他元素将是它的倍数。然后问题简化为,将大小为 g 的区间乘以 -1−任意次数,最大化 ∑ a i \sum a_{i} ai

让我们定义大小为 n(0 索引)的字符串 s=000…00,如果第 i 个元素不乘以 -1 ,则si=0,否则 si =1 。该操作翻转大小为 g 的子字符串中的所有 s的值。
让我们将 fx定义为所有 si值的异或,其中 i mod g =x,注意 fx的取值 大小为0≤x≤g−1。
在任何操作中,f 的所有值都会同时变化,因为它们在开始时都是 0,只有使所有 fi 相等的 s 的状态是可达的。
证明所有fi相等的s的所有状态都是可达的
让我们从 s的任何状态开始,使得 f=000…00 并重复选择最右边的 i,使得 si=1 和i≥g−1,并翻转以该位置结尾的子字符串,在尽可能多地这样做之后,对于 g−1≤i≤n−1,si=0.对于任何 0≤i<g,si=1.那么 fi=1 这是一个矛盾,因为 fg-1=0并且所有 fi同时变化,则 s=000…00。f 的所有值都等于 1 的情况类似

在此之后,可以使用 dp解决问题。
令dp[i][0]为ai,ai-g,ai-2g,…,ai-kg的最大值,其中:
在这里插入图片描述
同理dp[i][1]为:ai-g,ai-2g,…,ai-kg的最大值,其中:
在这里插入图片描述
最终答案为:
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    int T;
    cin >> T;

    while(T--)
    {
        int n,m;
        cin >> n >> m;
        vector<int> a(n),b(m);
        for(int i=0;i<n;i++)
            cin >> a[i];

        int g=0;
        for(int i=0;i<m;i++)
        {
            cin >> b[i];
            g=__gcd(g,b[i]);
        }

        vector<vector<ll>> dp(g,vector<ll>(2));
        for(int i=0;i<g;i++)
            dp[i][1]=-2e9;
        for(int i=0;i<n;i++)
        {
            int rem=i%g;
            ll v0=max(dp[rem][0]+a[i],dp[rem][1]-a[i]);
            ll v1=max(dp[rem][0]-a[i],dp[rem][1]+a[i]);
            dp[rem][0]=v0;
            dp[rem][1]=v1;
        }

        ll sum0=0,sum1=0;
        for(int i=0;i<g;i++)
        {
            sum0+=dp[i][0];
            sum1+=dp[i][1];
        }

        cout << max(sum0,sum1) << '\n';
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值