CSP-S模拟14(UU Round#2)
CSP-S模拟14(UU Round#2)
T1 100pts T2 0pts(哎呀呀,骇死我力) T3 10pts rk7
T1 划分序列(divide)
原题 : CF1919C - Grouping Increases
CF1919C Grouping Increases
题目描述
给定一个大小为 \(n\) 的数组 \(a\)。你需要按照以下过程计算你的惩罚值:
- 将数组 \(a\) 分成两个(可能为空)子序列 \(^\dagger\) \(s\) 和 \(t\),使得 \(a\) 的每个元素都属于 \(s\) 或 \(t^\ddagger\)。
- 对于一个大小为 \(m\) 的数组 \(b\),定义数组 \(b\) 的惩罚值 \(p(b)\) 为满足 \(b_i < b_{i+1}\) 的下标 \(i\)(\(1 \leq i \leq m-1\))的数量。
- 你最终获得的总惩罚值为 \(p(s) + p(t)\)。
如果你最优地执行上述过程,求你能获得的最小惩罚值。
\(^\dagger\) 序列 \(x\) 是序列 \(y\) 的子序列,如果 \(x\) 可以通过从 \(y\) 中删除若干(可能为零或全部)元素得到。
\(^\ddagger\) 一些将数组 \(a=[3,1,4,1,5]\) 分成 \((s,t)\) 的合法方式有 \(([3,4,1,5],[1])\),\(([1,1],[3,4,5])\) 和 \(([\,],[3,1,4,1,5])\),而一些不合法的方式有 \(([3,4,5],[1])\),\(([3,1,4,1],[1,5])\) 和 \(([1,3,4],[5,1])\)。
第一眼求最小惩罚值,dp 和 贪心都可做 ,那咋搞呢?
dp的状态是2维的 ,不太好转移, \(O(n^2)\) - > $ O(nlog(n)) $ 似乎需要用线段树,码量较大 ,所以考虑贪心
我们考虑什么时候能使答案最小:
这里记lastl , lastr的意思是 两个序列中最后一个元素
当 \(a[i]\) \(>=\) \(a[i - 1]\) 时 :
-
如果lastl 和 lastr 都能让 \(a[i]\) 不产生贡献 我们肯定先把小的更新掉,大的留给后面,一定更优
-
否则的话,我们将能 \(lastr\) 和 \(lastl\) 中比 \(a[i]\) 大的那个更新掉
当 \(a[i]\) \(<\) \(a[i - 1]\) 时 :
方案和上面类似
-
如果lastl 和 lastr 都能让 \(a[i]\) 产生贡献 , 那么我们肯定让 两者更小的更新 , ans++
-
否则的话 , 我们像上面一样同时考虑没有贡献的情况就ok了
体感难度黄左右,但是线段树优化dp的做法得有绿
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 20 ;
int a[N] , n , T ;
int main(){
freopen("divide.in","r",stdin);
freopen("divide.out","w",stdout);
ios :: sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
cin >> T ;
while(T--){
cin >> n ;
for(int i = 1 ; i <= n ; i++){
cin >> a[i] ;
}
int lastl = a[1] , lastr = 0x3f3f3f3f , ans = 0 ;
for(int i = 2 ; i <= n ; i++){
if( a[i] > a[i - 1] ){
if(lastl >= a[i]){
lastl = a[i] ;
}
else if( lastr >= a[i] ){
lastr = a[i] ;
}
else{
if( lastr < lastl ){
lastr = a[i];
ans++;
}
else{
lastl = a[i];
ans++;
}
}
}
else if( a[i] <= a[i - 1] ){
if(lastl >= a[i] and lastr >= a[i]){
if( lastl >= lastr ){
lastr = a[i] ;
}
else{
lastl = a[i] ;
}
}
else if( lastl >= a[i] ){
lastl = a[i] ;
}
else{
lastr = a[i] ;
}
}
}
cout << ans << endl ;
}
return 0;
}
T2 ZZH与背包(knapsack)
哎呀呀骇死我力这个题
本来这个题我秒出思路结果看错数据范围了,喜提0pts(数组开大了)
逆天赛时错解 数组开大MLE了qwq , \(O(nV)\) , 背包求方案 + 前缀和优化
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 60 ;
const int M = 1e8 + 10 ;
int a[N] , sum[M] , f[M] , L[910] , R[910] , cnt , n , q , l , r ;
signed main(){
freopen("knapsack.in","r",stdin);
freopen("knapsack.out","w",stdout);
ios :: sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
cin >> n >> q ;
if( q == 0 ){
return 0 ;
}
for(int i = 1 ; i <= n ; i++){
cin >> a[i] ;
}
int cnt = 0 ;
for(int i = 1 ; i <= q ; i++){
cin >> L[i] >> R[i] ;
cnt = max( cnt , R[i]) ;
}
f[0] = 1;
for(int i = 1 ; i <= n ; i++){
for(int j = cnt ; j >= a[i] ;j--){
f[j] += f[j - a[i]];
}
}
for(int i = 1 ; i <= cnt ; i++)sum[i] = sum[i - 1] + f[i] ;
for(int i = 1 ; i <= q ; i++){
cout << sum[R[i]] - sum[L[i] - 1] << endl ;
}
return 0 ;
}
n <= 40 显然可以meet in the middle
懒得讲了,直接贴代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n , q ;
int a[60] , tt1 , tt2, l , r , mid ;
int res1[1050000] ,res2[1050000] ;
inline void dfs1(int x , int sum){
if( x > mid ){
res1[++tt1] = sum ;
return ;
}
dfs1(x + 1 , sum);
dfs1(x + 1 , sum + a[x]);
}
inline void dfs2(int x , int sum){
if( x > n ){
res2[++tt2] = sum ;
return ;
}
dfs2(x + 1 , sum);
dfs2(x + 1 , sum + a[x]);
}
signed main(){
// freopen("data.in","r",stdin);
freopen("knapsack.in","r",stdin);
freopen("knapsack.out","w",stdout);
ios :: sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
cin >> n >> q ;
for(int i = 1 ; i <= n ; i++){
cin >> a[i] ;
}
mid = n / 2 ;
dfs1(1 , 0);
dfs2(mid + 1 , 0 );
sort( res1 + 1 , res1 + 1 + tt1 );
sort( res2 + 1 , res2 + 1 + tt2 );
res1[tt2 + 1] = 1e18;
res2[0] = -1e18 , res2[tt2 + 1] = 1e18;
for(int i = 1 ; i <= q ; i++){
cin >> l >> r ;
int ans = 0 ;
for(int p = 1 , ll = tt2 + 1 , rr = tt2 + 1; p <= tt1; ++p){
while(res1[p] + res2[ll] >= l) --ll;
while(res1[p] + res2[rr] > r) --rr;
ans += rr - ll;
}
cout << ans << endl ;
}
return 0 ;
}
T3
会了,懒得改
T4
不会

浙公网安备 33010602011771号