A. United We Stand
题意:
给定一个长度为
n
n
n 的正整数数组
a
a
a
把
a
a
a 中的所有元素分配到
b
b
b 和
c
c
c 中,要求:
- b b b 和 c c c 不空
- ∀ i ∈ [ 1 , l b ] , j ∈ [ 1 , l c ] , c j ∤ b i \forall i\in [1,l_b], j\in [1,l_c],\quad c_j \nmid b_i ∀i∈[1,lb],j∈[1,lc],cj∤bi
输出符合条件的 b b b 、 c c c
思路:
观察可以发现:可以直接把
a
a
a 中的最大值放到
c
c
c 中,因为
c
j
∣
b
i
→
c
j
≤
b
i
c_j \mid b_i \rightarrow c_j \leq b_i
cj∣bi→cj≤bi
如果
c
c
c 里面只有一种元素(
a
a
a 中的最大值),那么显然是符合条件的
// Problem: A. United We Stand
// Contest: Codeforces - Codeforces Round 892 (Div. 2)
// URL: https://codeforces.com/contest/1859/problem/0
// Memory Limit: 256 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
const int INF=0x3f3f3f3f;
const long long INFLL=0x3f3f3f3f3f3f3f3fLL;
typedef long long ll;
const int N=200;
int a[N],b[N],c[N];
void solve(){
int n;
std::cin>>n;
int cnt=0,maxv=0;
fore(i,1,n+1){
std::cin>>a[i];
if(a[i]>maxv){
maxv=a[i];
cnt=1;
}
else if(a[i]==maxv) ++cnt;
}
if(cnt==n){
std::cout<<-1<<endl;
return;
}
std::cout<<n-cnt<<' '<<cnt<<endl;
fore(i,1,n+1)
if(a[i]!=maxv) std::cout<<a[i]<<" ";
std::cout<<endl;
fore(i,1,cnt+1) std::cout<<maxv<<" ";
std::cout<<endl;
}
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int t;
std::cin>>t;
while(t--){
solve();
}
return 0;
}
B. Olya and Game with Arrays
题意:
有
n
n
n 个数组,每个数组有
m
i
m_i
mi 个数字,每个数组最多可以移走一个数字到其他组,同一个组可以接受多个数字
定义
b
e
a
u
t
y
:
∑
i
=
1
n
m
i
n
j
=
1
m
i
a
i
j
beauty: \sum_{i=1}^{n} min_{j=1}^{m_i} a_{ij}
beauty:∑i=1nminj=1miaij
求出最大 b e a u t y beauty beauty
思路:
要注意到:一个数组中,只有最小值和次小值会对答案有贡献,因为每个数组最多只能移走一个数字
并且将一个数组中的数字移动到另外一个数组,不会增加那个数组最小值对答案的贡献,只有可能减少
对于所有数组中的最小的那个值
m
i
n
v
minv
minv,把它移动到那个数组,那个数组对答案的贡献都是
m
i
n
v
minv
minv
当这个数组中最小的值被移走了,次小值就成为了对答案的贡献
因此可以考虑把
m
i
n
v
minv
minv 移动到次小值最小的那个数组中,这样答案就是
m
i
n
v
+
s
u
m
−
m
i
n
′
minv + sum - min^\prime
minv+sum−min′
s
u
m
sum
sum 是所有数组中次小值之和,
m
i
n
′
min^\prime
min′ 是最小的次小值
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
const int INF=0x3f3f3f3f;
const long long INFLL=0x3f3f3f3f3f3f3f3fLL;
typedef long long ll;
const int N=25050;
ll minv;
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int t;
std::cin>>t;
while(t--){
minv=INF;
int n;
std::cin>>n;
std::vector<ll> minsec(n);
int m;
fore(i,0,n){
std::cin>>m;
std::vector<ll> v(m);
fore(j,0,m) std::cin>>v[j];
ll temp=*min_element(v.begin(),v.end());
minv=std::min(minv,temp);
v.erase(find(v.begin(),v.end(),temp));
minsec[i]=*min_element(v.begin(),v.end());
}
std::cout<<minv+accumulate(minsec.begin(),minsec.end(),0ll)-*min_element(minsec.begin(),minsec.end())<<endl;
}
return 0;
}
C. Another Permutation Problem
题意:
对于所有长度为
n
n
n 的排列,求出
(
∑
i
=
1
n
p
i
∗
i
)
−
(
m
a
x
j
=
1
n
p
j
∗
j
)
(\sum_{i=1}^{n}p_i*i ) - (max_{j=1}^{n} p_j*j)
(∑i=1npi∗i)−(maxj=1npj∗j) 的最大值
思路:
打表发现符合条件的排列一定是先上升:
1
,
2
,
3....
1,2,3....
1,2,3....,然后到了某个位置以
n
n
n 递减:
n
,
n
−
1
,
n
−
2....
n,n-1,n-2....
n,n−1,n−2....,一直到末尾
直接枚举这个位置就好
// Problem: C. Another Permutation Problem
// Contest: Codeforces - Codeforces Round 892 (Div. 2)
// URL: https://codeforces.com/contest/1859/problem/C
// Memory Limit: 256 MB
// Time Limit: 3000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
const int INF=0x3f3f3f3f;
const long long INFLL=0x3f3f3f3f3f3f3f3fLL;
typedef long long ll;
inline int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return f*x;
}
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int t;
std::cin>>t;
while(t--){
int n;
std::cin>>n;
std::vector<int> a(n+5);
fore(i,1,n+1) a[i]=i;
int ans=0;
for(int p=n;p>=1;--p){
int v=n;
fore(i,p,n+1) a[i]=v--;
int res=0,maxv=0;
fore(i,1,n+1){
res+=i*a[i];
maxv=std::max(maxv,i*a[i]);
}
ans=std::max(ans,res-maxv);
}
std::cout<<ans<<endl;
}
return 0;
}
D. Andrey and Escape from Capygrad
题意:
给定
n
n
n 个线段,每个线段有
l
i
,
r
i
,
a
i
,
b
i
(
1
≤
l
i
≤
a
i
≤
b
i
≤
r
i
≤
1
0
9
)
l_i,r_i,a_i,b_i \quad(1 \leq l_i \leq a_i \leq b_i \leq r_i \leq 10^9)
li,ri,ai,bi(1≤li≤ai≤bi≤ri≤109),表示在
[
l
i
,
r
i
]
[l_i,r_i]
[li,ri] 的点可以传送到
[
a
i
,
b
i
]
[a_i,b_i]
[ai,bi] 中的任何一个点
q q q 次询问,每次询问给定一个坐标 x i x_i xi ,要求回答从这个坐标出发能到达的最远位置
思路:
考虑扫描线,从最右边开始往左边扫描
不难注意到,每一次传送一定是传送到
b
i
b_i
bi ,这样是最优的
而一个区间
b
i
→
r
i
b_i \rightarrow r_i
bi→ri 这一段如果只有这个区间的话,肯定是留在原地更好,如果有别的区间,就要考虑能不能去到更远的地方
所以我们只要注意三个变量:
b
i
,
x
i
,
l
i
b_i, x_i , l_i
bi,xi,li
并且这三个变量的优先级 是:
b
i
>
x
i
>
l
i
b_i > x_i > l_i
bi>xi>li
可以参考第三个样例,必须先更新一些能传送的点或者答案
定义
a
n
s
[
i
]
ans[i]
ans[i] :表示第
i
i
i 个区间能到的最远位置
定义
q
u
e
r
y
[
i
]
query[i]
query[i] :表示每个询问的答案,初始化为原始坐标
x
i
x_i
xi
使用 m u l t i s e t multiset multiset 来实现扫描活动
// Problem: D. Andrey and Escape from Capygrad
// Contest: Codeforces - Codeforces Round 892 (Div. 2)
// URL: https://codeforces.com/contest/1859/problem/D
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<iostream>
#include<vector>
#include<algorithm>
#include<set>
#include<unordered_set>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
const int INF=0x3f3f3f3f;
const long long INFLL=0x3f3f3f3f3f3f3f3fLL;
typedef long long ll;
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int t;
std::cin>>t;
while(t--){
int n;
std::cin>>n;
std::vector<std::tuple<int,int,int>> event;
std::vector<int> ans(n);
fore(i,0,n){
int l,r,a,b;
std::cin>>l>>r>>a>>b;
ans[i]=b;
event.emplace_back(b,3,i); //第二个元素是活动种类
event.emplace_back(l,1,i); //注意第二个元素的标号必须注意优先级
}
int q;
std::cin>>q;
std::vector<int> query(q); //答案
fore(i,0,q){
int x;
std::cin>>x;
query[i]=x; //初始化为原始位置
event.emplace_back(x,2,i);
}
std::sort(event.begin(),event.end());
reverse(event.begin(),event.end());
std::multiset<int> s;
for(auto [x,type,id] : event){
if(type==3){ //bi
if(!s.empty()) ans[id]=*s.rbegin();
s.insert(ans[id]);
}
else if(type==1){ //li
s.extract(ans[id]); //只删除一个,用erase会全部删除
}
else{ //xi
if(!s.empty()) query[id]=std::max(query[id],*s.rbegin());
}
}
for(auto el:query) std::cout<<el<<' ';
std::cout<<endl;
}
return 0;
}
E. Maximum Monogonosity
题意:
给定两个长度为
n
n
n 的数组
a
a
a 和
b
b
b,定义一个区间
[
l
,
r
]
[l,r]
[l,r] 的
c
o
s
t
cost
cost :
- c o s t = ∣ b l − a r ∣ + ∣ b r − a l ∣ cost = |b_l-a_r| + |b_r-a_l| cost=∣bl−ar∣+∣br−al∣
求出最大的不相交的区间的 ∑ c o s t \sum cost ∑cost ,并且这些区间的长度和等于 k k k
思路:
考虑
D
P
DP
DP: 定义区间
[
l
,
r
]
[l,r]
[l,r] 的
c
o
s
t
cost
cost :
f
(
l
,
r
)
=
∣
b
l
−
a
r
∣
+
∣
b
r
−
a
l
∣
f(l,r) = |b_l-a_r| + |b_r-a_l|
f(l,r)=∣bl−ar∣+∣br−al∣
那么对于
d
p
[
n
1
]
[
k
1
]
dp[n1][k1]
dp[n1][k1],状态转移方程可以写成:
d
p
[
n
1
]
[
k
1
]
=
m
a
x
(
d
p
[
n
1
−
1
]
[
k
1
]
,
d
p
[
n
1
−
i
]
[
k
1
−
i
]
+
f
(
n
1
−
i
+
1
,
n
1
)
,
1
≤
i
≤
k
1
)
dp[n1][k1] = max(dp[n1-1][k1],dp[n1-i][k1-i]+f(n1-i+1,n1),1\leq i \leq k1)
dp[n1][k1]=max(dp[n1−1][k1],dp[n1−i][k1−i]+f(n1−i+1,n1),1≤i≤k1)
方程的第二个式子表示:从
n
1
n1
n1 开始往前连续选
i
i
i 个元素作为一个区间,然后再在前面选
k
1
−
i
k1-i
k1−i 个元素
这样的时间复杂度是
O
(
n
k
2
)
O(nk^2)
O(nk2) ,会
T
L
E
TLE
TLE
考虑优化,发现当 n 1 − k 1 = d i a g v a l n1-k1 = diag_val n1−k1=diagval 这样一个特定值时,方程的第二个式子会重复计算这条对角线上的最大值:
因此可以维护一下这条对角线上的值,从而加速状态转移
把 c o s t cost cost 的两个绝对值拆开,有四种情况:
- b l − a l − a r + b r b_l - a_l -a_r + b_r bl−al−ar+br
- b l + a l − a r − b r b_l + a_l - a_r -b_r bl+al−ar−br
- − b l − a l + a r + b r -b_l - a_l + a_r + b_r −bl−al+ar+br
- − b l + a l + a r − b r -b_l + a_l +a_r - b_r −bl+al+ar−br
考虑分别在线维护四种情况的最大值 m x 1 , m x 2 , m x 3 , m x 4 mx_1,mx_2,mx_3,mx_4 mx1,mx2,mx3,mx4
注意到我们应该尽可能使前面两个数之和更大,因为每次最后两个数就是对应状态转移方程第二个式子的区间右端点 ( a n 1 , b n 1 ) (a_{n1},b_{n1}) (an1,bn1) ,是固定的,我们只能选择最后一个区间的左端点,使得四个 m x mx mx 值尽可能大
注意 i 、 j i 、j i、j 一定要从 0 0 0 开始遍历,否则无法处理 n 1 = k 1 n1=k1 n1=k1 即区间全选的情况
时间复杂度优化到: O ( n 2 ) O(n^2) O(n2)
// Problem: E. Maximum Monogonosity
// Contest: Codeforces - Codeforces Round 892 (Div. 2)
// URL: https://codeforces.com/contest/1859/problem/E
// Memory Limit: 512 MB
// Time Limit: 2500 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
const int INF=0x3f3f3f3f;
const long long INFLL=0x3f3f3f3f3f3f3f3fLL;
typedef long long ll;
void solve(){
int n,k;
std::cin>>n>>k;
std::vector<std::vector<ll>> dp(n+5,std::vector<ll>(k+5,0));
std::vector<ll> a(n+5),b(n+5);
fore(i,1,n+1) std::cin>>a[i];
fore(i,1,n+1) std::cin>>b[i];
std::vector<ll> mx1(n+5,-INFLL);
std::vector<ll> mx2(n+5,-INFLL);
std::vector<ll> mx3(n+5,-INFLL);
std::vector<ll> mx4(n+5,-INFLL);
fore(i,0,n+1)
fore(j,0,std::min(i,k)+1){
int diag_val=i-j;
if(i){
dp[i][j]=std::max(dp[i-1][j],-a[i]+b[i]+mx1[diag_val]); //这里要先和上一轮比较,直接从前i-1选j个
dp[i][j]=std::max(dp[i][j],-a[i]-b[i]+mx2[diag_val]);
dp[i][j]=std::max(dp[i][j],a[i]+b[i]+mx3[diag_val]);
dp[i][j]=std::max(dp[i][j],a[i]-b[i]+mx4[diag_val]);
}
/* 更新对角最值 */
mx1[diag_val]=std::max(mx1[diag_val],dp[i][j]-a[i+1]+b[i+1]);
mx2[diag_val]=std::max(mx2[diag_val],dp[i][j]+a[i+1]+b[i+1]);
mx3[diag_val]=std::max(mx3[diag_val],dp[i][j]-a[i+1]-b[i+1]);
mx4[diag_val]=std::max(mx4[diag_val],dp[i][j]+a[i+1]-b[i+1]);
}
std::cout<<dp[n][k]<<endl;
}
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int t;
std::cin>>t;
while(t--){
solve();
}
return 0;
}