CSP-S模拟14
\(T1:\)CF1919C Grouping Increases(划分序列(\(divide\)))
思路:
挺简单一道题赛时非得把它想得很难😥挂打分惹(╥﹏╥)。这题的正解有两种:线段树优化\(dp\)和贪心。作为一个懒人,我果断选择放弃线段树优化\(dp\),投入了贪心的怀抱(只找到一篇线段树优化的题解,给你们粘过来了~)。先读题:题目要求那一大坨的式子值最小,所以要尽量使 \(s\) 和$ t$ 呈单调不增,故序列的最后一个数显然越大越好,因为尾数越大,其他数能够加入此序列(且不产生任何代价)的机会就越多。我们设\(s\)数组的最后一位为\(x\),\(t\)数组的最后一位为\(y\)。我们不妨令\(x≤y\),然后我们遍历数组\(a\)。此时的\(a[i]\)可以分为三种情况:
-
\(a[i]≥x且a[i]≥y\). 这时的\(a[i]\)无论放到哪里都没有代价。但此时将\(a[i]\)放入结尾数字较小的数组更优,因为这样可以使得后续可以插入的元素范围更大。
-
\(a[i]≤x,a[i]≥y\). 这时的\(a[i]\)放到\(s\)数组没有代价。
-
\(a[i]≥x,a[i]≤y\). 这时的\(a[i]\)放到\(s\)数组没有代价。
-
\(a[i]≤x且a[i]≤y\). 这时的\(a[i]\)无论放到哪里都有代价。但此时将\(a[i]\)放入结尾数字较小的数组更优,因为这样可以使得后续可以插入的元素范围更大。
代码:
#include<iostream>
using namespace std;
const int N=2e5+5;
int T,n,a,ans,x,y;
int main(){
freopen("divide.in","r",stdin);
freopen("divide.out","w",stdout);
ios::sync_with_stdio(false);
cin>>T;
while(T--){
cin>>n;ans=0;x=y=1e7;//多组数据初始化
for(int i=1;i<=n;i++){
cin>>a;
if(x>=a&&y>=a)
if(x>y) y=a;
else x=a;//1.
else if(a<=x) x=a;//2.
else if(a<=y) y=a;//3.
else
if(x<y) x=a,ans++;
else y=a,ans++;//4.
}
cout<<ans<<'\n';//完结撒花
}
return 0;
}
\(T2:\)U142789 【1123T2】ZZH与背包
思路:
先看数据范围,嗯?\(1≤n≤40\),有点手痒,想打搜索肿么办?经过教练的一通折磨,瞬间想到了折半搜索。但是为啥赛时没拿分捏?因为光想搜了,不知道具体咋搜,所以挂了。
现在让我们站在神犇的肩膀上再来看这道题。我们到底要搜什么呢?显然,题目先给出了各个物品的体积,且这些体积不会随询问而变化,所以我们可以先预处理出这些体积的物品可以组成体积为多少种总体积不同的组合。这个预处理就可以用折半搜索。
然后把各种情况进行排序方便后续操作。然后用\(a\)中的答案与\(b\)中的答案求和,如果总答案在\(l\) ~ \(r\)的范围内,则合法。最后把合法的区间加和就好了~~
代码:
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
const int N=50;
int n,q,v[N],a[1049000],b[1049000],l1,l2,l,r,ans;
inline void dfs(int id,int sum){
if(id==(n>>1)){
a[++l1]=sum;//记录答案
return ;
}dfs(id+1,sum+v[id+1]);dfs(id+1,sum);//选 VS 不选
}//前半程搜索
inline void DFS(int id,int sum){
if(id==n){
b[++l2]=sum;
return ;
}DFS(id+1,sum+v[id+1]);DFS(id+1,sum);
}//后半程搜索同上
signed main(){
// freopen("knapsack.in","r",stdin);
// freopen("knapsack.out","w",stdout);
ios::sync_with_stdio(false);
cin>>n>>q;
for(int i=1;i<=n;i++) cin>>v[i];
dfs(0,0);DFS(n>>1,0);//折半搜索
sort(a+1,a+1+l1);sort(b+1,b+1+l2);//排序
while(q--){
ans=0;//多组数据
cin>>l>>r;
for(int aa=1,bb=l2,cc=l2;aa<=l1;aa++){
while(a[aa]+b[bb]>=l&&bb>0) bb--;//遍历第一个满足条件的位置
while(a[aa]+b[cc]>r&&cc>0) cc--;//遍历最后一个满足条件的位置
ans+=cc-bb;//区间加和
}
cout<<ans<<'\n';//完结撒花~~
}
return 0;
}