我永远喜欢珂朵莉~

题目

题目背景
http://sukasuka-anime.com/

戒不掉的珂毒

出不动的分块

哦对了有没有想买BD的珂学家啊?支持一下墨鱼吧~

或者有没有人想来手办众筹啊?

题目描述
给珂朵莉一个长为n的非负数序列a,支持以下两个操作:

1 l r x : 把区间[l,r]中所有x的倍数/x

2 l r : 查询区间[l,r]的和

珂朵莉很可爱,所以你要帮珂朵莉写这个题

输入格式
第一行两个数表示n,m

第二行n个非负整数表示ai

之后m行每行一个操作

1 l r x : 把区间[l,r]中所有x的倍数/x

2 l r : 查询区间[l,r]的和

输出格式
对于每次询问,输出一行一个数表示答案

输入输出样例
输入 #1复制
5 3
1 2 3 4 5
2 1 5
1 1 5 2
2 1 5
输出 #1复制
15
12
说明/提示
1 <= n , m <= 100000

0 <= ai <= 500000

1 <= x <= 500000

思路

可以发现一个数最多被/log次(无视掉1和0的情况)
考虑怎么维护操作1
500000以内的有最多约数的数有200个约数
然后可以用平衡树来维护
把每个i插入ai的所有约数对应的平衡树里面
每次区间[l,r]中x的倍数/x的时候
则在第x个平衡树里面把区间[l,r]截出来然后DFS这个子树
边DFS边删掉里面所有ai/x后不为x倍数的下标i
平衡树访问连续size个数的复杂度为logn+size的

代码

#include<bits/stdc++.h>
#define re register
using namespace std;
typedef long long ll;
char IN[1<<17],*SS=IN,*TT=IN;
inline char gc(){return (SS==TT&&(TT=(SS=IN)+fread(IN,1,1<<17,stdin),SS==TT)?EOF:*SS++);}
inline int read()
{
    int now=0,f=1;re char c=gc();
    for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now*f;}
const int maxn=100003;
const int maxk=500003;
int n,Q,A[maxn];
vector<int> T[maxk];
vector<int>::iterator pos1,pos2,it;
vector<vector<int>::iterator > be_to_del;
namespace BLT
{
    ll tr[maxn];
    inline int lowbit(int x){return x&(-x);}
    inline void add(int x,int k){while(x<=n)tr[x]+=k,x+=lowbit(x);}
    inline ll sum(int x,ll s=0){while(x)s+=tr[x],x-=lowbit(x);return s;}
    inline ll query(int l,int r){return sum(r)-sum(l-1);}
}
using namespace BLT;
inline void work(int pos,int x)
{
    for(re int i=1;i<=sqrt(x);i++)
        {
            if(x%i)continue;
            T[i].push_back(pos);
            if(x/i!=i)T[x/i].push_back(pos);
        }
}
inline void Clean()
{
    vector<vector<int>::iterator> tmp;
    swap(tmp,be_to_del);
}
inline void change(int l,int r,int k)
{
    if(k==1||T[k].empty())return ;
    pos1=lower_bound(T[k].begin(),T[k].end(),l);
    pos2=upper_bound(T[k].begin(),T[k].end(),r);
    Clean();
    for(it=pos1;it!=pos2;++it)
        {
            if(A[*it]%k)continue;
            add(*it,-(A[*it]-A[*it]/k)),A[*it]/=k;
            if(A[*it]%k)be_to_del.push_back(it);
        }
    if(be_to_del.empty())return ;
    for(re int i=be_to_del.size()-1;i>=0;i--)
        T[k].erase(be_to_del[i]);
}
int main()
{
    n=read(),Q=read();
    for(re int i=1;i<=n;i++)
        A[i]=read(),add(i,A[i]),work(i,A[i]);
    while(Q--)
        {
            int flag=read();
            if(flag==1)
                {
                    int x=read(),y=read(),k=read();
                    change(x,y,k);
                }
            else if(flag==2)
                {
                    int x=read(),y=read();
                    printf("%lld\n",query(x,y));
                }
        }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值