Codeforces Round #768 (Div. 2) A~D
比赛链接:Codeforces Round #768 (Div. 2)
A - Min Max Swap
题目大意:给出长度相同的a,b数组,你可以任意无限次的交换ai,bi(下标相同),问a数组最大乘b数组最大的结果最小能为多少。
解题思路:贪心思想,假设数组长度为n,那么在这2n个数里满足题目要求的情况是第1大的数*第n+1大的数,只需要将将所有的ai,bi比较,将较大的存入a数组,较小的存入b数组,则最后结果一定最小。
代码:
#include<string>
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int N = 2e5+5;
int a[110],b[110],n;
void solve()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
scanf("%d",&b[i]);
if(a[i]<b[i])swap(a[i],b[i]);
}
int maxx1=0,maxx2=0;
for(int i=1;i<=n;i++)
{
maxx1 = max(maxx1,a[i]);
maxx2 = max(maxx2,b[i]);
}
printf("%d\n",maxx1*maxx2);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)solve();
return 0;
}
B-Fun with Even Subarrays
题目大意:给定长度为n的数组,你可以选定长度为k的区间,将此区间之前的相邻的长度为k的区间变成相同数字。例如 1,2,3,4,5选择区间[3,4]那么操作之后数组变成3,4,3,4,5 但你不能选择[2,3]因为之前相邻的数组长度小于K。问最小的操作次数能使数组所有数相同。
解题思路:可以观察到,只能由后面的数改变前面的数,所以最后所有相同的那个数一定等于a[n],因为如果不等于a[n],那么你将无法改变a[n].
所以我们可以选择从后向前染色,若数字相同则跳过,直到遇到不等于a[n]的数字,假设下标为id,那么可以选择区间[id+1,n]来操作,直到a[1]=a[n]
代码:
#include<string>
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int N = 2e5+5;
int n,a[N];
void solve()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
int ans = 0;
for(int i=n;i>=2;i--)
{
if(a[i]==a[i-1])continue;
int id = i;
int len = n-i+1;
ans++;
for(int j=1;j<=len&&id-j>=1;j++)a[id-j] = a[n];
}
printf("%d\n",ans);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)solve();
return 0;
}
接下来CD两题由于博主太菜,比赛的时候未做出,赛后看了两位大佬的讲解视频才懂,略作讲解。
大佬视频:
bilibili Maystern
bilibili eroengine
C-And Matching
题目大意:给定n(n是2的幂次)和k(0<=k<n),n个数为(0~n-1) 求解将其划分成n/2组,有a1&b1+a2&b2+……+an/2&bn/2=k
解题思路:
可以观察到x&(n-1-x)=0 x&(n-1)=x
因为n是2的幂次,那么n-1二进制位上全是1
n=8 n=1000
n-1=7=0111
n-1减去x结果就是与按x位取反,
n-1&x的结果就是x
例如x=4=0100
n-1-x=0011 x&(n-1-x)=0
我们可以构造出这样的分组
0 n-1-0
1 n-1-1
2 n-1-2
……
n/2-1 n-n/2
对于每一对数&的结果都为0
1.于是我们可以将0与k对换位置
k&n-1 = k
0&n-1-k = 0
其他位置不变最终结果为所求K
2.若k = n-1需要采取另一种构造方法
n-1&n-2获得除2^0位上的所有1 res = n-2
再使1&n-1-2 获得2^0上的1 res = 1
以上两种分组之和为k,使0&2其他分组不变
注意若n=4 k=n-1 无解
代码:
#include<string>
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int N = 1e5+5;
int n,k;
int ans[N/2][2];
void solve()
{
scanf("%d%d",&n,&k);
if(n==4&&k==3)//唯一的无解
{
printf("-1\n");
return ;
}
for(int i=0;i<=n/2-1;i++)//构造出所有组合&值都为0
{
ans[i][0] = i;
ans[i][1] = n-1-i;
}
if(k==n-1)//特判
{
ans[0][0] = n-2;
ans[1][1] = n-3;
ans[2][1] = 0;
}
else
{
if(k<=n/2-1)swap(ans[0][0],ans[k][0]);
else swap(ans[0][0],ans[n-1-k][1]);
}
for(int i=0;i<=n/2-1;i++)
printf("%d %d\n",ans[i][0],ans[i][1]);
return ;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)solve();
return 0;
}
D-Range and Partition
题目大意:求最小的(y-x)[x,y]x,y为左右区间将长度为n的数组分成k段,使每一段在区间[x,y]内的数的数量严格大于区间外的。
解题思路:由题可知,k段中每一段在区间的数的数量至少比不在区间内的数的数量多1。并且只要整个数组满足整个条件那么一定存在将数组分成k段仍然满足的方法。判断条件 设整个数组中在区间内的数的数量为num,不在区间内的为n-num,只要数组,满足num-(n-num)>=k即可
于是我们枚举区间的左边界,二分求解满足条件的最小右边界,最后遍历数组遇到满意的区间直接输出即可
代码:
#include<string>
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int N = 2e5+5;
int n,k,a[N];
int vis[N];
/*
求最小的(y-x)[x,y]x,y为左右区间
将长度为n的数组分成k段,使每一段在区间[x,y]内的数的数量严格大于区间外的
由题可知,k段中每一段在区间的数的数量至少比不在区间内的数的数量多1
并且只要整个数组满足整个条件那么一定存在将数组分成k段仍然满足的方法
判断条件 设整个数组中在区间内的数的数量为num,不在区间内的为n-num
只要num-(n-num)>=k即可
于是我们枚举区间的左边界,二分求解满足条件的最小右边界,最后遍历数组遇到满意的区间直接输出即可
*/
bool check(int l,int r)
{
int num = vis[r]-vis[l-1];
return num-(n-num)>=k;
}
void solve()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)vis[i] = 0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
vis[a[i]]++;//计数
}
for(int i=1;i<=n;i++)vis[i]+=vis[i-1];//前缀和统计区间计数
int x=1,y=n;
for(int i=1;i<=n;i++)//枚举左边界
{
int l = i,r = n;
int num = vis[r]-vis[l-1];
if(num-(n-num)<k)break;//不存在以i为左边界的合法区间
while(l<=r)//二分找到最小的合法右边界
{
int mid = l+r>>1;
num = vis[mid]-vis[i-1];//区间范围内的数
if(num-(n-num)>=k)r = mid - 1;
else l = mid+1;
}
if(l-i<y-x)
{
x = i;
y = l;
}
}
printf("%d %d\n",x,y);
for(int i=1,r=1;i<=n;i=r+1,r=i,k--)
{
if(k==1)
{
printf("%d %d\n",i,n);
return ;
}
int cnt1=0,cnt2=0;
while(true)
{
if(a[r]>=x&&a[r]<=y)cnt1++;
else cnt2++;
if(cnt1<=cnt2)r++;
else break;
}
printf("%d %d\n",i,r);
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)solve();
return 0;
}