peiwenjun's blog 没有知识的荒原

P5610 [Ynoi2013] 大学 题解

题目描述

给定一个长为 \(n\) 的非负整数序列 \(a\)\(m\) 次操作,每次操作为以下两种之一:

  • 1 l r x :将区间 \([l,r]\) 中所有 \(x\) 的倍数除以 \(x\)
  • 2 l r :查询区间 \([l,r]\) 的和。

数据范围

  • \(1\le n,m\le 10^5,0\le a_i\le 5\cdot 10^5,1\le x\le 5\cdot 10^5\) ,强制在线。

时间限制 \(\texttt{500ms}\) ,空间限制 \(\texttt{512MB}\)

分析

考虑到一个数最多只会被除 \(\log n\) 次,所以只要能做到快速找出需要修改的位置即可,区间求和可以树状数组维护。

注意到 \(5\cdot10^5\) 范围内因数个数最大的数 \(d(498960)=200\) ,所以对于每个 \(x\) ,可以开 vector 存下所有 \(x\) 的倍数。

注意暴力 \(\mathcal O(n\sqrt v)\) 分解质因数会超时,但我们可以预处理 \(a\) 中每个数的因数,再将 \(i\) 加入 \(a_i\) 所有因数对应的 vector 中,时空复杂度都是 \(\mathcal O(n\cdot d(v))\) 可以接受。

对于除法操作,特判 \(x=1\) ,然后 lower_bound 找到区间起点,对每个数执行操作一,如果不再是 \(x\) 的倍数则将其删除,直到区间终点。

vector 不直接支持删除,考虑用并查集代替,\(f\) 数组指向在它之后(包括自身)第一个未被删除的位置的编号。

并查集同样支持二分和遍历。

算上树状数组的开销,时间复杂度 \(\mathcal O(n\cdot d(v)+v\log v+(n+m)\log n)\) ,空间复杂度 \(\mathcal O(n\cdot d(v)+v\log v+m)\)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5,v=5e5,maxv=5e5+5,inf=1e9;
int l,n,q,r,opt;
ll res,c[maxn];
int a[maxn];
bool vis[maxv];
vector<int> d[maxv],id[maxv];
struct dsu
{
    vector<int> f;
    int find(int x)
    {
        if(f[x]==x) return x;
        return f[x]=find(f[x]);
    }
}t[maxv];
int read()
{
    int q=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') q=10*q+ch-'0',ch=getchar();
    return q;
}
void add(int x,int v)
{
    while(x<=n) c[x]+=v,x+=x&(-x);
}
ll sum(int x)
{
    ll res=0;
    while(x) res+=c[x],x-=x&(-x);
    return res;
}
int main()
{
    n=read(),q=read();
    for(int i=1;i<=n;i++) a[i]=read(),add(i,a[i]),vis[a[i]]=true;
    for(int i=1;i<=v;i++)
        for(int j=i;j<=v;j+=i)
            if(vis[j]) d[j].push_back(i);
    for(int i=1;i<=n;i++) for(auto u:d[a[i]]) id[u].push_back(i);
    for(int i=1;i<=v;i++)
    {
        id[i].push_back(inf),t[i].f.resize(id[i].size());
        for(int j=0;j<t[i].f.size();j++) t[i].f[j]=j;
    }
    while(q--)
    {
        opt=read(),l=read()^res,r=read()^res;
        if(opt==1)
        {
            int x=read()^res,cur=lower_bound(id[x].begin(),id[x].end(),l)-id[x].begin();
            if(x==1) continue;
            cur=t[x].find(cur);
            while(id[x][cur]<=r)
            {
                int pos=id[x][cur];
                if(!(a[pos]%x)) add(pos,a[pos]/x-a[pos]),a[pos]/=x;
                if(a[pos]%x) t[x].f[cur]=cur+1;
                cur=t[x].find(cur+1);
            }
        }
        else res=sum(r)-sum(l-1),printf("%lld\n",res);
    }
    return 0;
}

posted on 2022-07-12 15:00  peiwenjun  阅读(11)  评论(0)    收藏  举报

导航