ABC393E
给定数组 A A A,求包含元素 A i A_i Ai的大小为 k k k的子集中最大的最大公约数。
题解:
首先思考对于整个数组所有包含 k k k个元素的子集中最大的GCD是多少,可以怎么求。
我们发现,如果一个数 x x x,数组中如果存在至少 k k k个数是它的倍数,那么我们一定可以找到一个大小为 k k k的子集,使得 x x x是这个集合的公约数。
因为总的值域比较小,所以我们可以使用一个桶统计所有数字的出现次数,然后使用调和级数枚举的方式求出对于每个数 i i i,数组当中有多少数是它的倍数。
如果一个数 i i i,满足它的倍数个数在数组 A A A中的出现次数大于 k k k,那么对于所有 i i i的倍数,我们一定都能找到一个大小为 k k k的集合使得公约数是 i i i。所以我们可以维护一个ans数组, a n s i ans_i ansi表示如果选择了一个值为 i i i的数,最大的答案是多少。对于所有的 A i A_i Ai,直接输出对应的ans即可。
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
int n,k;
int a[2000005];
int cnt[1000005];
int sum[1000005];
int ans[2000005];
void solve(){cin>>n>>k;for(int i=1;i<=n;i++){cin>>a[i];cnt[a[i]]++;}for(int i=1;i<=1e6;i++){for(int j=i;j<=1e6;j+=i){sum[i]+=cnt[j];}}for(int i=1;i<=1e6;i++){if(sum[i]>=k)for(int j=i;j<=1e6;j+=i){ans[j]=max(ans[j],i);//ans[j]=i;}}for(int i=1;i<=n;i++){cout<<ans[a[i]]<<endl;}
}
signed main(){ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);solve();
}
ABC393F
给定数组 A A A, q q q次询问前缀 r i r_i ri中所有小于 x i x_i xi的数组成的最长上升子序列是多少。
考虑二分栈求最长上升子序列做法,栈中元素表示对应LCS为 i i i时最后一个数字的最小值,所以我们可以直接将所有询问离线之后在对应的栈上二分找到答案即可。
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define pii pair<int,int>
using namespace std;
int n,q;
int a[200005];
vector<pii>ask[200005];
void solve(){cin>>n>>q;for(int i=1;i<=n;i++)cin>>a[i];for(int i=1;i<=q;i++){int x,y;cin>>x>>y;ask[x].push_back({y,i});}vector<int>st(1,0);vector<int>ans(q+1,0);for(int i=1;i<=n;i++){int l=0,r=st.size()-1;while(l<=r){int mid=l+r>>1;if(st[mid]<a[i])l=mid+1;else r=mid-1;}if(l==st.size())st.push_back(a[i]);else st[l]=a[i];// cout<<i<<" "<<l<<"??\n";for(auto [y,id]:ask[i]){l=0,r=st.size()-1;while(l<=r){int mid=l+r>>1;if(st[mid]<=y)l=mid+1;else r=mid-1;}ans[id]=r;}}for(int i=1;i<=q;i++)cout<<ans[i]<<endl;
}
signed main(){ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);solve();
}