因为学习的其他人的算法,所以叫学习笔记。。。
题意 :
维护序列,支持下列两个操作 :
把区间
[
l
,
r
]
[l,r]
[l,r] 中所有
x
x
x 的倍数除以
x
x
x。
查询区间
[
l
,
r
]
[l,r]
[l,r] 的和。
强制在线。
n ≤ 1 0 5 , a ≤ 5 × 1 0 5 n\leq 10^5,a\leq 5\times 10^5 n≤105,a≤5×105 ,时限 0.5s \texttt{0.5s} 0.5s。
Solution:
伞兵题目 。
求和操作完全就是在误导往线段树上去想 (我太菜了)。
下面做法摘自 command_block
:
显然,只会有 O ( n log a ) O(n\log a) O(nloga) 次生效的除法操作,我们使用树状数组维护区间和,这部分复杂度为 O ( n log a log n ) O(n\log a\log n) O(nlogalogn)。
现在难点在于如何找到应该被除的数。
设 S d S_d Sd 为序列中所有为 d d d 的倍数的数的集合, S 1... a S_{1...a} S1...a 的大小总和是 ∑ d ( a ) = O ( n d ( a ) ) \sum d(a)=O(nd(a)) ∑d(a)=O(nd(a)) 的。而 d ( a ) m a x = 200 d(a)_{max}=200 d(a)max=200 。
当我们区间除 d d d 时,只需要在 S d S_d Sd 中查看。
现在问题变为 : 维护一个序列,支持每次删除一个 区间 内的数。
所以… set 暴力?
可以用并查集 + 链表来维护
链表太恶心了,直接并查集查下一个元素
复杂度 O ( n a + n log a log n ) O(n\sqrt{a}+n\log a\log n) O(na+nlogalogn)。
一些技巧:
Step 1
\text{Step 1}
Step 1
你发现暴力分解约数是
O
(
n
a
)
O(n\sqrt a)
O(na) 而非
O
(
n
a
3
)
O(n\sqrt [3] a)
O(n3a) ,会导致复杂度为
O
(
n
a
+
n
log
n
)
O(n\sqrt a+n\log n)
O(na+nlogn),于是改为筛子预处理因数。
复杂度下降至 O ( a ln a + n a 3 + n log a log n ) O(a\ln a+n\sqrt[3]{a}+n\log a\log n) O(alna+n3a+nlogalogn)。
Step 2
\text{Step 2}
Step 2
预处理因数时,不要用 vector 保存因数,自己写一个邻接表。
甚至发现只有 n < = 1 0 5 n<=10^5 n<=105 个数需要预处理因数,可以大大减小常数。
如果不加上述两条,会得到 98 p t s 98pts 98pts 的好成绩。
Step 3
\text{Step 3}
Step 3
发现每一个位置的下标数组和并查集数组的大小是固定的。
所以保存下标和并查集的时候不需要使用 vector,可以自己开一个内存池,用指针来模拟数组。
这一点是非常重要的。
#include <bits/stdc++.h>
#define ll long long
#define lbt(p) ((-p)&(p))
using namespace std;
ll read(){
ll X=0;char ch=0;
while(ch<48||ch>57)ch=getchar();
while(ch>=48&&ch<=57)X=X*10+(ch^48),ch=getchar();
return X;
}
const int MaxN=100500;
const int MaxA=500500;
const int mxt=6138450+500;
int n;ll t[MaxN];
int head[mxt],nxt[mxt],ver[mxt],tot;
void Add(int x,int y) {
ver[++tot]=y,nxt[tot]=head[x],head[x]=tot;
}
void add(int p,int x) {
while(p<=n) {
t[p]+=x; p+=lbt(p);
}
}
ll qry(int p) {
ll tot (0);
while(p) {
tot+=t[p]; p^=lbt(p);
}
return tot;
}
struct node{
int *f;
void Init(int n) {
for(int i=0;i<n;i++) f[i]=i;
}
int find(int u) {
return f[u]==u?u:f[u]=find(f[u]);
}
}T[MaxA];
int c[MaxA],*p[MaxA],o[MaxN*405],*tp=o,x[MaxN];
void div(int l,int r,int d) {
if(d==1) return;
l=lower_bound(p[d],p[d]+c[d],l)-p[d];
r=upper_bound(p[d],p[d]+c[d],r)-p[d]-1;
if(l>r) return;
for(int u=T[d].find(l);u<=r;) {
int to=p[d][u];
if(x[to]%d==0) {
add(to,x[to]/d-x[to]);
x[to]/=d;
}
if(u>=r) break;
if(x[to]%d) {
u=T[d].f[u]=T[d].find(u+1);
}
else u=T[d].find(u+1);
}
}
int q,m;
signed main() {
// freopen("data.in","r",stdin);
// freopen("own.out","w",stdout);
n=read(),q=read();
for(int i=1;i<=n;i++) {
add(i,x[i]=read());
m=max(m,x[i]);
c[x[i]]++;
}
for(int i=2;i<=m;i++) {
if(c[i]) Add(i,i);
for(int j=i+i;j<=m;j+=i) {
if(!c[j]) continue;
c[i]+=c[j];
Add(j,i);
}
}
// printf("YES\n%d\n",cnt);
for(int i=2;i<=m;i++) {
if(c[i]) {
p[i]=tp; tp+=c[i];
T[i].f=tp; T[i].Init(c[i]);
tp+=c[i]; c[i]=0;
}
}
for(int i=1;i<=n;i++) {
int t=x[i]; if(t<=1) continue;
for(int j=head[t];j;j=nxt[j]) {
int d=ver[j];
p[d][c[d]++]=i;
}
}
ll l,r,x,las=0;
for(int i=1,op;i<=q;i++){
op=read();l=read();r=read();
l^=las;r^=las;
if(op==1)div(l,r,read()^las);
else printf("%lld\n",las=qry(r)-qry(l-1));
}
}