E题
链接:https://ac.nowcoder.com/acm/contest/78292/E
来源:牛客网
小苯非常喜欢等比数列。有一天他得到了一个长为 \(n\) 的数组 \(a\),他想从里面选一些数字使得被选中的数字构成一个等比数列,请问他最多可以选择多少个数字呢
输入包含两行。
第一行一个正整数 \(n\ (1\leq n \leq 2 \times 10^5)\),表示数组 \(a\) 的长度。
第二行 \(n\) 个正整数 \(a_i \ (1 \leq a_i \leq 2 \times 10^5)\),表示数组 \(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都增加了多少逆序对,我们计算具体贡献
- 增加的是R后面比a[l]小的元素,
- 还有就是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;
}
标签:周赛,int,sum,ROUND38,牛客,vector,ans,指针,逆序
From: https://www.cnblogs.com/mathiter/p/18106057