P5610 解题报告
P5610 解题报告
简要题意
一个长为 \(n\) 的非负整数序列 \(a\),支持以下两个操作:
1 l r x:把区间 \([l,r]\) 中所有 \(x\) 的倍数除以 \(x\)。2 l r:查询区间 \([l,r]\) 的和。
本题强制在线。
数据范围: \(1\leq n,m\leq 10^5\),\(0\leq a_i\leq 5\times 10^5\), \(1\leq x\leq 5\times 10^5\),\(1\leq l\leq r\leq n\)。
时间限制:\(500\) ms。
分析
首先,我们考虑一个数至多被操作 \((\log V)\) 次,所以我们不需要关心操作部分的时间复杂度。因此我们只需要分析怎么找到需要被操作的数。
我们注意到:在 \([1,5\times 10 ^5]\) 内因数最多的个数为 \(200\)。所以我们开 \(5\times 10^5\) 个链表,然后我们就可以枚举一个数的所有因数,然后把 \(a_i\) 插入到每一个因数的链表中,我们就可以二分查找操作区间的左右端点,然后操作一个点的时候我们就判断这个点是否还是 \(x\) 的因数,如果不是,就把它删掉。删掉的操作可以使用并查集实现。询问操作就用常数最小的树状数组。
但是至此,我们还是做不了这题。我们还要做很多事情:
-
首先,我们枚举一个数的因数这部分对每一个数做是 \(O(n \sqrt n)\) 的,这并不优秀。我们考虑枚举因数,然后枚举它的倍数,这个时间复杂度是调和级数,即 \(O(n \log n)\)。
-
其次,
vector实在太慢了。我们只好自己实现链表:开一个内存池,然后数据就用指针,赋值之后就可以当普通数组用了。但是注意,这个内存池不要开太大,不然会增大常数(指针移动的时间)。
于是,我们整理思路,总结一下步骤:
-
开桶,然后预处理出每一个因数的出现次数,然后预留出每一个因数的链表空间;统计每一个数的因数个数并预留空间。
-
然后预处理每一个数的因数,然后对每一个序列中的元素插入因数。
-
在询问时,先二分出当前询问区间对应到当前链表上的区间,然后通过并查集实现访问下一项,每一次访问下一项时判断当前数是否还是当前数的倍数,不是就通过并查集删掉。
代码
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define Inf (1ll<<60)
#define For(i,s,t) for(int i=s;i<=t;++i)
#define Down(i,s,t) for(int i=s;i>=t;--i)
#define ls (i<<1)
#define rs (i<<1|1)
#define bmod(x) ((x)>=p?(x)-p:(x))
#define lowbit(x) ((x)&(-(x)))
#define End {printf("NO\n");exit(0);}
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
inline void ckmx(int &x,int y){x=(x>y)?x:y;}
inline void ckmn(int &x,int y){x=(x<y)?x:y;}
inline void ckmx(ll &x,ll y){x=(x>y)?x:y;}
inline void ckmn(ll &x,ll y){x=(x<y)?x:y;}
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
inline ll min(ll x,ll y){return x<y?x:y;}
inline ll max(ll x,ll y){return x>y?x:y;}
char buf[1<<20],*p1,*p2;
#define gc() (p1 == p2 ? (p2 = buf + fread(p1 = buf, 1, 1 << 20, stdin), p1 == p2 ? EOF : *p1++) : *p1++)
#define read() ({\
int x = 0, f = 1;\
char c = gc();\
while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = gc();\
while(c >= '0' && c <= '9') x = x * 10 + (c & 15), c = gc();\
f * x;\
})
void write(int x){
if(x>=10) write(x/10);
putchar(x%10+'0');
}
const int N=1e5+100,V=5e5+100,K=180;
int n,T,m,a[N],*p[V],*b[V],c[V],ctp[V];
ull vl[N];
int pf_p[21496900],*tp_p=pf_p;
int pf_lst[21496900],*tp_lst=pf_lst;
int pf_DSU[21496900],*tp_DSU=pf_DSU;
inline void add(int x,int v){
// printf("(%d %d)\n",x,v);
while(x<=n) vl[x]+=v,x+=lowbit(x);
}
inline ll ask(int x){
ll res=0;
while(x) res+=vl[x],x-=lowbit(x);
return res;
}
struct DSU{
int *f;
inline void init(int n){
for(int i=0;i<n;++i)
f[i]=i;
}
inline int find(int x){
return f[x]^x ? (f[x]=find(f[x])) : x;
}
}d[V];
inline void solve(int l,int r,int x){
if(x==1) return;
l=lower_bound(b[x],b[x]+c[x],l)-b[x];
r=upper_bound(b[x],b[x]+c[x],r)-b[x]-1;
if(l>r) return;
// printf("? %d %d\n",l,r);fflush(stdout);
int nw=d[x].find(l);
// printf("! %d\n",nw);fflush(stdout);
while(nw<=r){
int id=b[x][nw];
// printf("! %d\n",id);fflush(stdout);
if(!(a[id]%x))
add(id,a[id]/x-a[id]),a[id]/=x;
if(nw>=r) break;
if(a[id]%x)
d[x].f[nw]=nw+1;
nw=d[x].find(nw+1);
}
}
int main()
{
#if !ONLINE_JUDGE
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
#endif
n=read(),T=read();
For(i,1,n) a[i]=read(),add(i,a[i]),m=max(m,a[i]),++c[a[i]];
For(i,2,m)
for(int j=(i<<1);j<=m;j+=i)
c[i]+=c[j],++ctp[j];
For(i,2,m)
if(c[i]){
d[i].f=tp_DSU;
d[i].init(c[i]);
tp_DSU+=c[i];
b[i]=tp_lst;
tp_lst+=c[i];
c[i]=0;
}
For(i,2,m){
p[i]=tp_p;
++ctp[i],tp_p+=ctp[i],ctp[i]=0;
}
For(i,2,m)
for(int j=i;j<=m;j+=i)
p[j][ctp[j]]=i,++ctp[j];
int v;
For(i,1,n)
for(int j=0;j<ctp[a[i]];++j)
v=p[a[i]][j],b[v][c[v]]=i,++c[v];
int opt,l,r,x;
ull lstans=0;
while(T--){
opt=read();
if(opt==1){
l=read()^lstans,r=read()^lstans,x=read()^lstans;
solve(l,r,x);
}
else{
l=read()^lstans,r=read()^lstans;
printf("%lld\n",lstans=(ask(r)-ask(l-1)));
}
}
return 0;
}

浙公网安备 33010602011771号