【学习笔记】[Ynoi2013] 大学

因为学习的其他人的算法,所以叫学习笔记。。。

题意 :

维护序列,支持下列两个操作 :

把区间 [ 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 n105,a5×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)); } }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530293.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(7)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示