水题
#include<bits/stdc++.h> #define clr(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=10010; const ll mod=1000000007; int n,x,p1,p2; int main(){ int T; cin>>T; while (T--){ p1=p2=0; cin>>n; for(int i=1;i<=n;i++){ scanf("%d",&x); if(x>p1){ p2=p1; p1=x; }else if(x>p2){ p2=x; } } printf("%d ",min(n-2,min(p1-1,p2-1))); } return 0; }
B.Pillars
题意:每个柱子上都有一个圆盘,当且仅当当前柱子上只有一个圆盘并且这个圆盘小于相邻的圆盘,才可以移动到那个圆盘上去,问是否能把所有圆盘移动到一根柱子上。
考虑到条件,会发现每个圆盘有意义的移动只有一次,移动后柱子上可能就不止一个圆盘了,所以合法序列必须是先递增后递减的,注意数组大小,wa3可能是re。
#include<bits/stdc++.h> #define clr(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=200010; const ll mod=1000000007; int n; int a[maxn]; int main() { while(cin>>n) { for(int i=1; i<=n; i++) { scanf("%d",&a[i]); } int p=0,flag=0; for(int i=2; i<=n; i++) { if(p==0&&a[i]>a[i-1]) { } else if(p==0&&a[i]<a[i-1]) { p=1; } else if(p==1&&a[i]<a[i-1]) { } else { flag=1; } } if(flag) { puts("NO"); } else { puts("YES"); } } }
题意:给出一个非递减序列,划分成k段,每一段的价值是当前段的最大值减最小值,要求总价值最小,求最小价值。
思路:如果没有划分成k段的条件,单独的原序列的价值其实就是相邻所有元素相减的和,现在要划分成k段,所以我们可以去掉这些相邻元素相减的k-1个最大值,其实就是在这k-1个位子上划一刀。
#include<bits/stdc++.h> #define clr(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=300010; const ll mod=1000000007; int n,k; ll a[maxn]; ll b[maxn]; int main(){ while(cin>>n>>k){ for(int i=1;i<=n;i++){ scanf("%lld",&a[i]); if(i>1){ b[i-1]=a[i]-a[i-1]; } } sort(b+1,b+1+n-1); ll ans=0; for(int i=1;i<=n-1-(k-1);i++){ ans+=b[i]; } printf("%lld ",ans); } }
D. Yet Another Subarray Problem
题意:给出一个序列,让你选择一个子区间,子区间的价值是题目中所给的式子,要求子区间的价值最大,求最大的价值是多少。
思路:最简单的方式就是暴力枚举所有的lr,而假设我们确定了一个r,考虑所有l,发现选择l/m的余数(也就是l%m)相同的左边界,所带来的收益变化就是损失了某一段区间和与添加了若干个k,所以我们比较一下这两个值,哪个大我们就取哪个左区间,这样的区间种类最多有m个,所以我们就分别枚举一下这m个最优左区间,并且维护这个东西就可以了。
#include<bits/stdc++.h> #define clr(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=300010; const ll mod=1000000007; ll n,m,k; ll a[maxn]; ll sum[maxn]; int ql[maxn]; int main() { while(cin>>n>>m>>k) { for(int i=1; i<=n; i++) { scanf("%lld",&a[i]); sum[i]=sum[i-1]+a[i]; } ll ans=0; for(int i=1; i<=n; i++) { if(ql[i%m]==0) { ql[i%m]=i; } else if(sum[i-1]-sum[ql[i%m]-1]<(i-ql[i%m])/m*k) { ql[i%m]=i; } for(int j=0; j<m; j++) { if(ql[j]) { ans=max(ans,sum[i]-sum[ql[j]-1]-k*(ll)(ceil((i-ql[j]+1)*1.0/m))); } } } printf("%lld ",ans); } }
题意:给出n个圆环的内半径和外半径,圆环之间互相套的条件就是圆环一的内半径大于等于圆环二的外半径,空出来的部分是剩余空间。现在要求所有极大子集中,最小剩余空间的方案数(极大子集的定义就是,不能再往这个子集中放入一个新的圆环了)
思路:先按找内半径从小到大排序,从后往前扫描,设$dp[o]$表示第i个圆环的最小剩余空间,要将一个圆环放入其他圆环中,最小剩余空间显然是用这个圆环和其他所有能包含此圆环的圆环的剩余空间减一下,取最小的那个,而由于我们已经排过序了,所以直接使$dp[i]$单调递减,然后二分出第一个能包含当前圆环的圆环,在dp[id]上进行处理即可。
因为排过序了,所以二分到的第一个圆环以及以后的所有圆环肯定都可以包含当前圆环,在这里面找最小值,所以用dp[i]保存当前最小空间是正确的。
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<ll,ll> pll; const int maxn=200010; ll p=1e9+7; pair<ll,ll>a[maxn]; ll dp[maxn],cnt[maxn]; int n; int main(){ while(cin>>n){ for(int i=1;i<=n;i++){ scanf("%lld%lld",&a[i].second,&a[i].first); } a[n+1].first=1e9+7; sort(a+1,a+1+n); dp[n+1]=1e9+7; for(int i=n;i>0;i--){ int id=lower_bound(a+i,a+n+1,(pair<ll,ll>){a[i].second,(ll)-1})-a; if(id==n+1){ dp[i]=a[i].first; cnt[i]=1; }else{ dp[i]=dp[id]-a[i].second+a[i].first; cnt[i]=cnt[id]; } if(dp[i]>dp[i+1]){ dp[i]=dp[i+1]; cnt[i]=cnt[i+1]; }else if(dp[i]==dp[i+1]){ cnt[i]=(cnt[i]+cnt[i+1])%p; } } printf("%lld ",cnt[1]); } }
现场做出四题时还剩三十多分钟,但是四道简单题wa了好多次,罚时超级高,第五题被两个人忽悠了个相同的错误题意(咋这么有缘),没做出,好久没打cf了。。cf现场赛降智商啊。。