Codeforces Round 892 (Div. 2)
Codeforces Round 892 (Div. 2)
A. United We Stand
简述题意
给定一个长度为 $ n $ 的数列 $ a $ ,要求将 $ a $ 的每个元素分配到数列 $ b $ ,$ c $ 中,满足以下两个要求
- $ b,c $ 不为空,即 $ l_b \geq 1 , l_c \geq 1 $ 。
- 对于任意 $ i $ 和 $ j $ $ (1\leq i \leq l_b ,1\leq j \leq l_c) $ ,$ c_j $ 不为 $ b_i $ 的约数 。
如果有分配方案满足两个条件,第一行输出$ l_b,l_c $ 接下来两行分别输出数列$ b,c $ 。
如果没有分配方案满足两个条件则输出-1
。
共有 $ T $ 组数据,$ T\leq500 $ ,$ 2 \leq n \leq 100 $ ,$ 1 \leq a_i \leq 10^9 $
思路
首先,对于 $ \forall x,y(x>y) $ , $ x $ 一定不为 $ y $ 的约数,基于这个事实,可以考虑将 $ a $ 中的所有最大值放进 $ b $ 中,其余的放进 $ a $ 中,这样便能满足条件2;但是有一种特殊情况便是 $ a $ 中只由一个数字组成,如果仍然依照上面的思路将所有最大值放进数组 $ b $ 中,就会导致 $ a $ 为空,如果在 $ a,b $ 皆不为空的情况下无论如何分配 $ a $ 中的元素均不能满足条件1,所以遇见这种特殊情况的时候直接输出-1
。
代码实现
没什么需要注意的,排序之后特判一下特殊情况,从后面往前面找到第一次出现最大值的位置 $ x $ , $ l_b $ 就为 $ x-1 $ ,$ l_c $ 则为 $ n-x+1 $ ,然后直接输出 $ b,c $ 即可。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const unsigned int N=105;
int n,a[N];
inline int r(){
int x=0,y=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')
y=-1;
ch=getchar();
}
while(isdigit(ch)){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*y;
}
bool cmp(int x,int y){
return x<y;
}
void solve(){
n=r();
for(register int i=1;i<=n;i++)
a[i]=r();
sort(a+1,a+n+1,cmp);
if(a[n]==a[1]){
puts("-1");
return;
}
int x=n;
for(register int i=n;i>1;i--){
if(a[i-1]==a[i]){
x=i-1;
continue;
}
break;
}
printf("%d ",x-1);
printf("%d\n",n-x+1);
for(register int i=1;i<=x-1;i++)
printf("%d ",a[i]);
printf("\n");
for(register int i=x;i<=n;i++)
printf("%d ",a[i]);
printf("\n");
}
signed main(){
int T=r();
while(T--)
solve();
}
B. Olya and Game with Arrays
简述题意
这里有 $ n $ 个数列,第 $ i $ 个数列包含 $ m_i $ 个正整数 。
现在你可以在每个数列中挑出至多一个元素移动至另外一个数列(当然也可以不移动),一个数列中可以接受多个元素加入进来,但是一个数列中只有一个元素可以被挑出移动至其他数列,所有操作在同一时刻完成 。
美丽值的定义是每个数列中的最小值之和,即 $ \sum_{i=1}^{n} min_{j=1}^{m_i}a_{i,j} $ 。
现在求通过操作后,这 $ n $ 个数列最大的美丽值 。
共有 $ T $ 组数据 , $ 1 \leq T \leq 25000 $ ,$ 1 \leq n \leq 25000 $ ,$ 2 \leq m \leq 50000 $ ,$ 1 \leq a_{i,j} \leq 10^9 $ 。
思路
我们先思考一下一个数列如何移动能让这次移动对答案的贡献最大,肯定是将数列中的最小值移出去,若第 $ i $ 个数列中的最小值为 $ x_i $,次小值为 $ y_i $ 那么将这个数列中的最小值移出去对答案的贡献是 $ y_i-x_i $ ,为了让答案尽可能的大,我们先假设每个数列都将它的次小值移出去,当前的答案是 $ \sum_{i=1}^{n}y_i $ ,但是必须要找到一个数列来接受这些被移出的最小值,我们发现,若第 $ i $ 个数列接受了这些最小值,答案将会减去 $ y_i - min_{i=1}^{n} x_i $ ,为了让答案最大,所以我们需要让 $ y_i $ 最小,所以最终的答案就是 $ \sum_{i=1}^{n}y_i -min_{j=1}^{n} y_j+min_{k=1}^{n}x_k $ 。
代码实现
由于影响答案的只有每个数列的最小值和次小值,所以在读入的时候记录每一个数列的最小值和次小值,在记录每个数列最小值的最小值和每个数列次小值的最小值 即可 ,注意多测清空,开long long
。
代码如下
#include<bits/stdc++.h>
#define int long long
#define INF (int)1e9+15
using namespace std;
const unsigned int N=25005;
int n,m,mxa[N],mxb[N];//mxa记录每个数列的最小值,mxb记录每个数列的次小值
inline int r(){
int x=0,y=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')
y=-1;
ch=getchar();
}
while(isdigit(ch)){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*y;
}
void solve(){
int ans=0,mm=INF;//mm记录所有数列中的最小值
n=r();
for(register int i=1;i<=n;i++){
m=r();
mxa[i]=mxb[i]=INF;
for(register int j=1;j<=m;j++){
int x=r();
mm=min(mm,x);
if(x<mxa[i]){
mxb[i]=mxa[i];
mxa[i]=x;
continue;
}
mxb[i]=min(x,mxb[i]);
}
}
for(register int i=1;i<=n;i++){
int tmp=mm;
for(register int j=1;j<=n;j++){
if(i==j)
continue;
//printf("tmp=%d\n",tmp);
tmp+=mxb[j];
}
ans=max(ans,tmp);
}
printf("%lld\n",ans);
return;
}
signed main(){
int T=r();
while(T--)
solve();
}
C.Another Permutation Problem
题意简述
给出一个 $ n $ ,你可以任意改变长度为 $ n $ 的排列中的元素位置使得这个排列的价值最大 。
长度为 $ n $ 排列的定义是 $ \forall i \in [1,n] $ , $ p_i \in [1,n] $ 且排列中没有重复出现的元素 。
排列 $ p $ 的价值的定义为 : 排列中的每一个元素的大小乘上它在排列中的位置减去排列中一个数乘上他所在的位置的最大值,即 $ (\sum_{i=1}^{n} p_i \cdot i)-(max_{j=1}^{n}p_j \cdot j) $ 。
求改变排列中元素的顺序后排列的最大值 。
共有 $ T $ 组数据 ,$ 1 \leq T \leq 30 $ , $ 2 \leq n \leq 250 $ , 保证每组数据 $ n $ 的总和不超过 $ 500 $ 。
思路
第一眼看见这道题没有任何思路,看样例感觉像是反转一下 $ p_n $ 和 $ p_{n-1} $ 的位置,但是通过暴力求全排列算出的答案发现 $ n=5 $ 时并不符合这个规律,在通过暴力求全排列求 $ n $ 更大时候的方案符合 $ p $ 为 $
1,2,3,...,x,n,n-1,n-2,...,x+2,x+1 $
观察 $ n $ 的大小,我们可以枚举 $ x $ 的位置,每次枚举后算出当前方案下排列的价值,取最大值输出即可,时间复杂度 $ O(n^2) $ 。
代码
注意多测清空
#include<bits/stdc++.h>
#define INF 0x7fffffff
using namespace std;
const unsigned int N=255;
int n,sol[N],tmp[N],ans,t_max=-INF;
bool vis[N];
inline int r(){
int x=0,y=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')
y=-1;
ch=getchar();
}
while(isdigit(ch)){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*y;
}
int get_sol(int x){//算出当前方案下排列的价值
int res=0;
t_max=-INF;
for(register int i=1;i<=x;i++){
res+=i*i;
t_max=max(t_max,i*i);
}
for(register int i=x+1;i<=n;i++){
res+=(n-(i-(x+1)))*i;
t_max=max(t_max,(n-(i-(x+1)))*i);
}
res-=t_max;
return res;
}
void solve(){
n=r();
ans=0;
for(register int i=0;i<=n;i++)
ans=max(get_sol(i),ans);
printf("%d\n",ans);
return;
}
signed main(){
int T=r();
while(T--)
solve();
}