牛客周赛ROUND38

E题

链接:https://ac.nowcoder.com/acm/contest/78292/E
来源:牛客网

小苯非常喜欢等比数列。有一天他得到了一个长为 n 的数组 a,他想从里面选一些数字使得被选中的数字构成一个等比数列,请问他最多可以选择多少个数字呢

输入包含两行。

第一行一个正整数 n (1n2×105),表示数组 a 的长度。
第二行 n 个正整数 ai (1ai2×105),表示数组 a 的元素。

solution:比较经典的不同范围不同做法,由于公比过大的时候会乘几次就结束,所以我们对此暴力。对于公比小的部分进行dp递推

#include<bits/stdc++.h> using namespace std; #define endl '\n' typedef long long ll; typedef pair<int, int> pii; typedef pair<ll, ll> pll; const int N = 2e5 + 5; inline void solve() { int n; cin >> n; vector<int> a(n + 5); vector<int> st(N, 0); for(int i=1; i<=n; i++) cin >> a[i], st[a[i]] = 1; sort(a.begin()+1, a.begin()+1+n); int ans = 1; vector<vector<int>> dp(N, vector<int>(101, 0));//dp[i][j]:以a[i]结尾的, 公比为j的等比数列的最大长度 //100n for(int i=1; i<=n; i++) { dp[a[i]][1] ++; ans = max(ans, dp[a[i]][1]); for(int j=2; j<=100; j++) {//公比 if(a[i] % j == 0) { if(st[a[i] / j]) dp[a[i]][j] = 2;//如果只选了这两个 dp[a[i]][j] = max(dp[a[i]][j], dp[a[i] / j][j] + 1); ans = max(ans, dp[a[i]][j]); } } } //分块 for(ll i=100; i<=2e5; i++) {//公比>100, 至多1~3次 //实际复杂度只有O(n) for(ll j=1; j*i<=2e5; j++) {//首项 if(!st[j]) continue; if(!st[j*i]) { //考虑有没有第二项 ans = max(ans, 1); continue; } if(j*i*i > 2e5 || !st[j*i*i]) { //考虑有没有第三项 ans = max(ans, 2); continue; } ans = max(ans, 3); break; } } cout << ans << endl; }

G:双指针维护逆序对

题目

链接:https://ac.nowcoder.com/acm/contest/78292/G
来源:牛客网

小红有一个长度为 n 的数组 a,他想从 a 中删除一段区间,但又要使得剩余的部分拼起来组成的新数组满足:逆序对个数不少于 k

她想知道有多少种删除方案,请你帮帮她吧。
注:空数组逆序对数量为0。

solution:首先本题就是双指针,只不过维护的是逆序对,比常规双指针增加难度

我们考虑固定R。对于L,由于是删除区间,左指针右移表示删除区间缩小,逆序对数量增加。
所以我们对于每一个r需要找到极限l,l满足恰好>=k。考虑每次移动l都增加了多少逆序对,我们计算具体贡献

  1. 增加的是R后面比a[l]小的元素,
  2. 还有就是L前面a[l]大的
  • 对于2的贡献我们只需要移动l的时候在线维护
  • 对于1的贡献由于指针是从左往右移动,所以我们只有提前维护后缀的值域树状数组,跟着r移动的时候在线删除维护,这样才可以
  • 每次删除右端点的元素减去贡献计算同理
int n, m; int a[N]; //首先本题就是双指针,只不过维护的是逆序对,比常规双指针增加难度 //我们考虑固定R。对于L,由于是删除区间,左指针右移表示删除区间 //缩小,逆序对数量增加。 //所以我们对于每一个r需要找到极限l,l满足恰好>=k。 //每次移动l都增加了多少逆序对,我们计算具体贡献 //1增加的是R后面比a[l]小的元素, //2.还有就是L前面a[l]大的 //对于2的贡献我们只需要移动l的时候在线维护 //对于1的贡献由于指针是从左往右移动,所以我们只有提前维护后缀的 //值域树状数组,跟着r移动的时候在线删除维护,这样才可以 template <typename T> struct Fenwick { int n; std::vector<T> a; Fenwick(int n_ = 0) { init(n_); } void init(int n_) { n = n_; a.assign(n, T{}); /* T{}是一种初始化T类型对象的方式,它使用了花括号初始化列表进行数值初始化。 对于大多数内置数据类型(如整数、浮点数),这将初始化为0。 */ } void add(int x, const T &v) { for (int i = x; i <=n-1; i += i & -i) { a[i] = a[i] + v; } } T sum(int x) { T ans{}; for (int i = x; i > 0; i -= i & -i) { ans = ans + a[i]; } return ans; } T rangeSum(int l, int r) {//要传入l-1 //待优化:直接给个vector记录前缀和 return sum(r) - sum(l); } int select(const T &k) {//寻找最后一个使得前缀和小于等于 k 的位置。 int x = 0; T cur{}; for (int i = 1 << std::__lg(n-1); i; i /= 2) {//GCC if (x + i <= n-1 && cur + a[x + i] <= k) { x += i; cur = cur + a[x]; } } return x; } }; void solve(){ cin>>n;cin>>m; for(int i=1;i<=n;i++)cin>>a[i]; Fenwick<int>suf(1e6+1),pre(1e6+1); int sum=0; for(int i=n;i>=1;i--){ suf.add(a[i],1); sum+=suf.sum(a[i]-1); //suf.add(a[i],1); } int ans=0; if(sum>=m)ans++; for(int i=1,j=1;i<=n;i++){ suf.add(a[i],-1); sum-=suf.sum(a[i]-1); sum-=pre.rangeSum(a[i],1000000); //suf.add(a[i],-1); while(j<=i&&sum<m){ pre.add(a[j],1); sum+=suf.sum(a[j]-1); sum+=pre.rangeSum(a[j],1000000); //pre.add(a[j],1); j++; } ans+=i-j+1; } cout<<ans<<endl; }

__EOF__

本文作者爱飞鱼
本文链接https://www.cnblogs.com/mathiter/p/18106057.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   potential-star  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示