【学习笔记】[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 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));
	}
}

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号