P5610 [Ynoi2013] 大学 题解
题目描述
给定一个长为 \(n\) 的非负整数序列 \(a\) , \(m\) 次操作,每次操作为以下两种之一:
1 l r x:将区间 \([l,r]\) 中所有 \(x\) 的倍数除以 \(x\) 。2 l r:查询区间 \([l,r]\) 的和。
数据范围
- \(1\le n,m\le 10^5,0\le a_i\le 5\cdot 10^5,1\le x\le 5\cdot 10^5\) ,强制在线。
时间限制 \(\texttt{500ms}\) ,空间限制 \(\texttt{512MB}\) 。
分析
考虑到一个数最多只会被除 \(\log n\) 次,所以只要能做到快速找出需要修改的位置即可,区间求和可以树状数组维护。
注意到 \(5\cdot10^5\) 范围内因数个数最大的数 \(d(498960)=200\) ,所以对于每个 \(x\) ,可以开 vector 存下所有 \(x\) 的倍数。
注意暴力 \(\mathcal O(n\sqrt v)\) 分解质因数会超时,但我们可以预处理 \(a\) 中每个数的因数,再将 \(i\) 加入 \(a_i\) 所有因数对应的 vector 中,时空复杂度都是 \(\mathcal O(n\cdot d(v))\) 可以接受。
对于除法操作,特判 \(x=1\) ,然后 lower_bound 找到区间起点,对每个数执行操作一,如果不再是 \(x\) 的倍数则将其删除,直到区间终点。
vector 不直接支持删除,考虑用并查集代替,\(f\) 数组指向在它之后(包括自身)第一个未被删除的位置的编号。
并查集同样支持二分和遍历。
算上树状数组的开销,时间复杂度 \(\mathcal O(n\cdot d(v)+v\log v+(n+m)\log n)\) ,空间复杂度 \(\mathcal O(n\cdot d(v)+v\log v+m)\) 。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5,v=5e5,maxv=5e5+5,inf=1e9;
int l,n,q,r,opt;
ll res,c[maxn];
int a[maxn];
bool vis[maxv];
vector<int> d[maxv],id[maxv];
struct dsu
{
vector<int> f;
int find(int x)
{
if(f[x]==x) return x;
return f[x]=find(f[x]);
}
}t[maxv];
int read()
{
int q=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') q=10*q+ch-'0',ch=getchar();
return q;
}
void add(int x,int v)
{
while(x<=n) c[x]+=v,x+=x&(-x);
}
ll sum(int x)
{
ll res=0;
while(x) res+=c[x],x-=x&(-x);
return res;
}
int main()
{
n=read(),q=read();
for(int i=1;i<=n;i++) a[i]=read(),add(i,a[i]),vis[a[i]]=true;
for(int i=1;i<=v;i++)
for(int j=i;j<=v;j+=i)
if(vis[j]) d[j].push_back(i);
for(int i=1;i<=n;i++) for(auto u:d[a[i]]) id[u].push_back(i);
for(int i=1;i<=v;i++)
{
id[i].push_back(inf),t[i].f.resize(id[i].size());
for(int j=0;j<t[i].f.size();j++) t[i].f[j]=j;
}
while(q--)
{
opt=read(),l=read()^res,r=read()^res;
if(opt==1)
{
int x=read()^res,cur=lower_bound(id[x].begin(),id[x].end(),l)-id[x].begin();
if(x==1) continue;
cur=t[x].find(cur);
while(id[x][cur]<=r)
{
int pos=id[x][cur];
if(!(a[pos]%x)) add(pos,a[pos]/x-a[pos]),a[pos]/=x;
if(a[pos]%x) t[x].f[cur]=cur+1;
cur=t[x].find(cur+1);
}
}
else res=sum(r)-sum(l-1),printf("%lld\n",res);
}
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/16470110.html
浙公网安备 33010602011771号