三晋七校第一届新生赛(同步赛)
C
令\(x=i\times j\),\(f(x)=p=(i\times j)^2=x^2\)(\(i,j\geq 1\)),\(f(x)\)的所有取值构成可重集和\(S\),同时\(S\)为单调不减的,现在求\(S\)中前\(k\)小的元素之和。
因为\(k\)比较大,显然我们不能直接暴力枚举。我们尝试模拟一下会发现:
x=1,(i,j)组合1种
x=2,(i,j)组合2种
x=3,(i,j)组合2种
......
x=v,(i,j)组合等于cnt[fac[v]]
也就是x=v的(i,j)组合数等于x的因数个数
令\(g(x)=\sum_{i=1}^x{cnt[i]}\),其中\(cnt[i]\)表示\(i\)的因数个数。我们需要找到最大的那个\(x\),满足\(g(x)\leq k\),这个可以通过二分答案,然后每次\(check\)使用分块解决,时间复杂度为\(O(\sqrt{V}\log {V})\)。
找到上界后,我们需要解决\(\sum_{i=1}^{x}{i^2\times cnt[i]}\),也就是\(\sum_{i=1}^{x}\sum_{d=1}^{i}{i^2}\times [d\mid i]\),如果这样写表示每次枚举数\(num\),然后枚举其因数,时间复杂度为\(O(V\times \sqrt{V})\),显然会爆掉。
我们交换一下枚举顺序:\(\sum_{d=1}^{x}\sum_{k=1}^{\lfloor\frac{x}{d}\rfloor}{(kd)^2}=\sum_{d=1}^{x}d^2\sum_{k=1}^{\lfloor\frac{x}{d}\rfloor}{k^2}\),令\(m=\lfloor\frac{x}{d}\rfloor\),那么\(\sum_{k=1}^{m}{k^2}=\frac{n\times (n+1)\times(2\times n+1)}{6}\)。
我们可以根据\(d\)对\(\lfloor\frac{x}{d}\rfloor\)进行分块,也就是\(\lfloor\frac{x}{l}\rfloor=\lfloor\frac{x}{r}\rfloor\),然后对于相同的\(m\),令\(h(x)=\sum_{i=1}^x{i^2}\),式子变为\(\sum{(h(r)-h(l-1)\times h(m))}\)。
注意要及时取模就好,最后我们再加上\((k-g(x))\times x^2\)就好。
#include <bits/stdc++.h>
using namespace std;
#define inf 1e18
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
const int N = 2e5 + 9, M = 2e5 + 9, mod = 998244353;
//求[1,n]的约数个数之和
int inv_6;
int f(int n){
int sum=0;
for(int l=1,r;l<=n;l=r+1){
r=n/(n/l);
sum+=n/l*(r-l+1);
}
return sum;
}
int ksm(int b,int p){
int res=1;
while(p){
if(p&1) res=res*b%mod;
b=b*b%mod;
p>>=1;
}
return res;
}
//g(n)为[1,n]所有数平方和
int g(int n){
return n%mod*(n%mod+1)%mod*(n%mod*2%mod+1)%mod*inv_6%mod;
}
void solve() {
int k;
cin >> k;
inv_6=ksm(6,mod-2);
int lo=1,hi=1e12;
int lim=1;
//求
auto check=[&](int mid)->bool{
return f(mid)<=k;
};
while(lo<=hi){
int mid=(lo+hi)/2;
if(check(mid)){
lim=mid;
lo=mid+1;
}else hi=mid-1;
}
// cout << lim << endl;
//找到了上限,对前面求和
int ans=0;
for(int l=1,r;l<=lim;l=r+1){
r=lim/(lim/l);
int m=lim/l;
ans=(ans+g(m)*((g(r)-g(l-1)+mod)%mod)%mod)%mod;
}
int r=k-f(lim);
ans=(ans+r*(lim%mod+1)%mod*(lim%mod+1)%mod)%mod;
cout << ans << endl;
}
/*
*/
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
H
最大化\(mex\oplus gcd\),很经典的讨论就是枚举\(mex\),我们只需要讨论\(mex=0,1\)和\(mex>1\)的三种情况即可。
这道题有一个前置知识:
给定一个序列\(a\),现在有不可重集合\(S\)以及\(a\)的子集\(A\),那么\(s=(\sum_{v\in A}{v})\in S\),也就是说任选一个\(a\)的子集,其子集元素和\(sum\)是\(S\)中的元素,求\(a\)的子集合最大不能表示的数\(mex\)。
这个可以通过排序+贪心来解决,我们先对\(a\)进行排序,之后假设现在能够表示\([0,x]\),那么可以讨论现在枚举的数\(a_i\):
- 如果\(a_i>x+1\),那么\(mex=x+1\);
- 否则可表示范围为\([0,x+a_i]\);
这个时间复杂度是\(O(n\log{n})\)的。
sol:
当\(mex=0\)时,\(ans=sum[r]-sum[l-1]\)。
当\(mex=1\)时,\(ans=(sum[r]-sum[l-1])\oplus 1\)。
当\(mex\geq 2\)时,\(gcd=1\),我们只需要最大化\(mex\)就行,\(ans=mex\oplus 1\)。
最后答案肯定在三种情况中的一种,前面只需要维护前缀和以及0的个数即可。
对于第三个,因为有\(q\)个询问,如果每次排序然后他贪心选择的话,时间复杂度为\(O(qn\log{n})\),显然不行。那么根据我们的前置知识,我们可以考虑使用数据结构来优化这个。
这里有一个很神奇的结论,就是这里\(mex\)的变化次数最多\(log{V}\)次,说是和斐波拉契数列的变化同阶。所以使用可持久化线段树的总复杂度为\(n\log^2{V}\)。
法一:可持久化线段树
我们枚举最大覆盖值\(x\),那么每次查询在\([l,r]\)中,值小于等于\(x+1\)的元素之和\(res\),如果\(res=x\),那么最大覆盖值无法再增大,否则\(x=res\)。肯定有的是\(res\geq x\),所以当\(res=x\)时,我们退出。
#include <bits/stdc++.h>
using namespace std;
#define inf 1e18
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
const int N = 1e5 + 9, M = 2e5 + 9, mod = 1e9 + 7;
int root[N],tot;
int lc[N*32],rc[N*32],sum[N*32];
int a[N];
vector<int> v;
int n,m;
int build(int l,int r){
int id=++tot;
if(l==r) return id;
int m=l+r>>1;
lc[id]=build(l,m);
rc[id]=build(m+1,r);
return id;
}
int update(int prev,int l,int r,int pos,int add){
int id=++tot;
lc[id]=lc[prev];
rc[id]=rc[prev];
sum[id]=sum[prev]+add;
if(l==r) return id;
int m=l+r>>1;
if(pos<=m) lc[id]=update(lc[prev],l,m,pos,add);
else rc[id]=update(rc[prev],m+1,r,pos,add);
return id;
}
int query(int x,int y,int l,int r,int ql,int qr){
if(ql>qr) return 0;
if(ql<=l&&r<=qr) return sum[y]-sum[x];
int m=l+r>>1;
int res=0;
if(ql<=m) res+=query(lc[x],lc[y],l,m,ql,qr);
if(qr>m) res+=query(rc[x],rc[y],m+1,r,ql,qr);
return res;
}
void solve() {
cin >> n;
vector<int> sm(n+1),zero(n+1);
for(int i=1;i<=n;i++){
cin >> a[i];
v.push_back(a[i]);
sm[i]=sm[i-1]+a[i];
zero[i]=zero[i-1]+(a[i]==0?1:0);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
auto get_id=[&](int x){
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
};
vector<int> id(n+1);
for(int i=1;i<=n;i++){
id[i]=get_id(a[i]);
}
int rsize=v.size();
root[0]=build(1,rsize);
for(int i=1;i<=n;i++){
root[i]=update(root[i-1],1,rsize,id[i],a[i]);
}
cin >> m;
while(m--){
int l,r;
cin >> l >> r;
if(zero[r]-zero[l-1]==0){
cout << sm[r]-sm[l-1] << endl;
continue;
}
int s=0;
while(1){
//查询区间[l,r]小于等于need的值的和
int pos=upper_bound(v.begin(),v.end(),s+1)-v.begin();
int res=query(root[l-1],root[r],1,rsize,1,pos);
if(res==s) break;
s=res;
}
int mex=s+1;
int ans=sm[r]-sm[l-1];
if(mex>=1) ans=max(ans,1^(sm[r]-sm[l-1]));
if(mex>1) ans=max(ans,1^mex);
cout << ans << endl;
}
}
/*
*/
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
法二:离线莫队+树状数组
我们对原问题进行离线,之后用树状数组维护前缀和,单次修改为\(\log{n}\),单次查询为\(\log{n}\),那么总时间复杂度应该为\(O(q\log{n}+n\sqrt{n}\log{n})\),按道理后面那个完全会超呀。但是这道题就是可以过,这就是\(mex\)的神奇性质嘛。
但是还是非常极限了时间879ms,所以还是用可持久化线段树好一点。
#include <bits/stdc++.h>
using namespace std;
#define inf 1e18
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
const int N = 2e5 + 9, M = 2e5 + 9, mod = 1e9 + 7;
struct Fenwick{
vector<int> sum;
int n;
Fenwick(int n){this->n=n;sum.resize(n+1);}
int lowbit(int x){return x&-x;}
void update(int pos,int k){for(int i=pos;i<=n;i+=lowbit(i)) sum[i]+=k;}
int query(int pos){int res=0;for(int i=pos;i;i-=lowbit(i)) res+=sum[i];return res;}
};
void solve() {
int n,m;
cin >> n;
vector<int> a(n+1),zero(n+1),sum(n+1);
for(int i=1;i<=n;i++){
cin >> a[i];
sum[i]=sum[i-1]+a[i];
zero[i]=zero[i-1]+(a[i]==0);
}
vector<int> b=a;
sort(b.begin(),b.end());
b.erase(unique(b.begin(),b.end()),b.end());
int rsize=b.size();
vector<int> id(n+1);
auto get_id=[&](int x){
return lower_bound(b.begin(),b.end(),x)-b.begin()+1;
};
for(int i=1;i<=n;i++){
id[i]=get_id(a[i]);
}
cin >> m;
vector<array<int,3>> qu(m+1);
for(int i=1;i<=m;i++){
cin >> qu[i][0] >> qu[i][1];
qu[i][2]=i;
}
int B=sqrt(n)+1;
sort(qu.begin()+1,qu.end(),[&](array<int,3> x,array<int,3> y){
auto [l1,r1,id1]=x;
auto [l2,r2,id2]=y;
if((l1-1)/B!=(l2-1)/B) return (l1-1)/B<(l2-1)/B;
else return r1<r2;
});
Fenwick tr(rsize);
auto del=[&](int pos){
tr.update(id[pos],-a[pos]);
};
auto add=[&](int pos){
tr.update(id[pos],a[pos]);
};
auto get=[&](int l,int r){
int res=sum[r]-sum[l-1];
if(zero[r]-zero[l-1]==0){
return res;
}
int s=0;
while(1){
int pos=upper_bound(b.begin(),b.end(),s+1)-b.begin();
int sm=tr.query(pos);
if(sm==s) break;
s=sm;
}
int mex=s+1;
res=max(res,res^1);
if(mex>1) res=max(res,mex^1);
return res;
};
int cl=1,cr=0;
vector<int> ans(m+1);
for(int i=1;i<=m;i++){
auto[l,r,id]=qu[i];
while(l<cl) add(--cl);
while(cr<r) add(++cr);
while(l>cl) del(cl++);
while(r<cr) del(cr--);
ans[id]=get(l,r);
}
for(int i=1;i<=m;i++) cout << ans[i] << endl;
}
/*
*/
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
最后讨论一下为什么这里求\(mex\)的次数最多\(\log{V}\)次。
看一个例子:
初始:[0,-1]
加入0:[0,0]
加入1:[0,1]
加入2:[0,3]
加入4:[0,7]
加入8:[0,15]
很明显了,如果当前覆盖区间是\([0,x]\),那么下一次我们找的是\(x+1\),那么下一次覆盖范围为\([0,2\times x+1]\),这个就能够看出来了。
A
给定\(n\)个数排成一行,每5个数一组,将前面4个丢弃,保留第5个数,直到剩下的数小于4个,问最后剩下几个数,分别是多少。
模拟一下可以知道,当前是\(n\),那么剩下的数为\(n/5+n\%5\),我们记下每一轮的\(Q_j,r_j\),这样我们可以倒推:
如果当前剩下\(m\)个数,那么对于\(idx\in[1,m]\),可以分为下面两种情况:
- 如果\(idx\le Q_j\),那么\(idx\)是整数分组中的,那么前一轮的位置为\(5\times idx\);
- 如果\(idx>Q_j\),那么\(idx\)是最后余下的几个数中的,那么前一轮的位置为\(5\times Q_j+(idx-Q_j)\);
所以我们直接按照这个模拟就行,时间复杂度为\(O(\log{n})\)。
#include <bits/stdc++.h>
using namespace std;
#define inf 1e18
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
const int N = 2e5 + 9, M = 2e5 + 9, mod = 1e9 + 7;
void solve() {
int n;
cin >> n;
vector<int> num,Q;
num.push_back(n);
while(num.back()>=4){
int v=num.back();
int q=v/5;
Q.push_back(q);
int r=v%5;
num.push_back(q+(r==4?0:r));
}
vector<int> ans;
for(int i=1;i<=num.back();i++){
int idx=i;
for(int j=Q.size()-1;j>=0;j--){
if(idx<=Q[j]) idx=idx*5;
else idx=Q[j]*5+(idx-Q[j]);
}
ans.push_back(idx);
}
cout << ans.size() << endl;
for(int i:ans) cout << i << " ";
cout << endl;
}
/*
*/
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}

浙公网安备 33010602011771号