大致题意: 给定一个序列,支持两种操作:把一个区间中(x)的倍数都除以(x);询问区间和。
(O(sqrt n) ightarrow O(logn) ightarrow O(alpha(n)))
一道并不难的(Ynoi),然而因为各种原因写了一个多小时。。。
最初,看到(Ynoi),本能反应就是根号,于是就写了个愚蠢的根号做法,毫无悬念地(T)了。
然后,想到可以使用(set),结果没想到被卡得和根号做法同分。
最后,无可奈何去参考题解,才发现可以使用并查集,终于过了此题。(尽管预处理的复杂仍然是(O(nsqrt V))的,询问的复杂度也依旧是(O(nlogn)))
解题思路
首先,一个暴力的思路,我们考虑对于每一个数(x),开个(set)存储所有是其倍数的元素的下标。
每次我们只要到(x)的(set)中找到在修改区间内的下标(k),枚举(a_k)原先的因数判断哪些因数不再是它的因数,并在这些因数对应的(set)中删去(k)。
考虑到一个数每被操作一次,至少会变成原先的一半,因此最多被操作(log V)次,但每一次操作还要枚举因数,复杂度难以接受。
然后我们发现,其实每次修改没必要立刻把(k)从不再是(a_k)因数的元素的(set)中删去,只需在每次访问到(a_k)时判断其是否为当前操作数(x)的倍数即可,若不是则将其从(set)中删去。
虽然复杂度已经得到极大优化,但仍旧难以接受。考虑到这里的(set)只有删除,而没有加入操作,完全可以用(vector+)并查集替代。
具体地,对于每一个(vector)(原(set))开一个并查集,每当删去一个元素,就在并查集上将它和后一个元素合并,具体实现详见代码。
最后讲讲区间求和操作,由于我们把区间修改转化成了单点修改,直接开树状数组维护即可。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define V 500000
#define pb push_back
using namespace std;
int n,a[N+5],C[V+5];vector<int> P[V+5],f[V+5];
struct TreeArray//树状数组
{
long long a[N+5];I void U(RI x,CI v) {W(x<=n) a[x]+=v,x+=x&-x;}
I long long Q(RI x,long long t=0) {W(x) t+=a[x],x-=x&-x;return t;}
}A;
I int fa(CI v,CI x) {return ~f[v][x]?f[v][x]=fa(v,f[v][x]):x;}//并查集
int main()
{
#define PB(x,i) (P[x].pb(i),f[x].pb(-1))//往vector中加入元素,同时扩展并查集大小
RI Qt,i,j;for(scanf("%d%d",&n,&Qt),i=1;i<=n;++i)//读入
{
scanf("%d",a+i),A.U(i,a[i]),PB(a[i],i);
for(j=2;j*j<=a[i];++j) !(a[i]%j)&&(PB(j,i),j^(a[i]/j)&&(PB(a[i]/j,i),0));//枚举因数
}
for(i=1;i<=V;++i) C[i]=P[i].size(),f[i].pb(-1);
RI op,sz,k;long long l,r,x,t=0;W(Qt--)
{
scanf("%d%lld%lld",&op,&l,&r),l^=t,r^=t;//强制在线
if(op==2) {printf("%lld
",t=A.Q(r)-A.Q(l-1));continue;}//询问
if(scanf("%lld",&x),(x^=t)==1) continue;//x=1忽略
#define G(x,v) (lower_bound(P[x].begin(),P[x].end(),v)-P[x].begin())//二分
for(i=fa(x,G(x,l));i^C[x]&&(k=P[x][i])<=r;i=fa(x,i+1))//枚举vector中在修改区间内的位置
a[k]%x?(f[x][i]=fa(x,i+1),0):(A.U(k,a[k]/x-a[k]),a[k]/=x);//如果不是x的倍数就删去,否则将其除以x
}return 0;
}