CodeForces - 920F SUM and REPLACE (线段树)

题意:给N个数M次操作,(1<=N,M<=3e5, 1<=ai<=1e6),1是使[L,R]中的每个元素变成其因子的个数之和;2是求[L,R]区间之和

分析:看上去就很线段树的一题,但是却思考了很久。发现1和2即使对其,也不会改变二者的值。而且一个大于2的数进行多次1操作,也最终会退化到2。

先预处理筛出1e6以内各数的质因子个数和。在线段树的节点中维护两个值:区间和以及区间最大值。在update函数中,如果该区间的最大值不超过2,那么该区间没有更新的必要;若超过2,则递归向下找到那个位置,并更新它。

听起来像是退化成了单点更新线段树,其实打个表能发现每个数的因子个数和不会很大,退化几次就成为2了,所以在更新次数很多的情况下,复杂度并不会很高。

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
const int INF =0x3f3f3f3f;
const int maxv = 1e6+5;
typedef long long LL;
struct SGndoe{
    LL sum;
    LL mx;
}tree[maxn<<4];
LL a[maxn];
LL ans[maxv];

void pre()
{
    for(int i=1;i<=1e6;++i){
        for(int j=0;j<=1e6;j+=i){
            ans[j]++;
        }
    }
}

void pushup(int root)
{
    tree[root].sum =tree[root<<1].sum+tree[root<<1|1].sum;
    tree[root].mx = max(tree[root<<1].mx,tree[root<<1|1].mx);
}

void build(int root,int L,int R){
    if(L==R){
        tree[root].sum = tree[root].mx=a[L];
        return;
    }
    int mid =(L+R)>>1;
    build(root<<1,L,mid);
    build(root<<1|1,mid+1,R);
    pushup(root);
}

void update(int root,int l,int r,int L,int R)
{
    if(L<=l && R>=r && tree[root].mx<=2) return;
    else if(l==r){
        tree[root].sum = ans[tree[root].sum];
        tree[root].mx = tree[root].sum;
        return;
    }
    int mid=(l+r)>>1;
    if(L<=mid)update(root<<1,l,mid,L,R);
    if(R>mid) update(root<<1|1,mid+1,r,L,R);
    pushup(root);
}

LL query(int root,int l,int r,int L,int R)
{
    if(L<=l&&r<=R) return tree[root].sum;
    int mid=(l+r)>>1;
    LL res=0;
    if(L<=mid) res+=query(root<<1,l,mid,L,R);
    if(mid<R) res+=query(root<<1|1,mid+1,r,L,R);
    return res;
}

int main(){
    int T,N,M,num,t,x;
    int L,R;
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    #endif
    pre();
    int cas=1;
    while(scanf("%d%d",&N,&M)==2){
        for(int i=1;i<=N;++i) scanf("%lld",&a[i]);
        build(1,1,N);
        int op;
        for(int i=1;i<=M;++i){
            scanf("%d%d%d",&op,&L,&R);
            if(op==1) update(1,1,N,L,R);
            else printf("%lld\n",query(1,1,N,L,R));
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/xiuwenli/p/9387607.html

时间: 2024-07-31 05:35:01

CodeForces - 920F SUM and REPLACE (线段树)的相关文章

codeforces CF920F SUM and REPLACE 线段树 线性筛约数

$ \Rightarrow $ 戳我进CF原题 F. SUM and REPLACE time limit per test: 2 seconds memory limit per test: 256 megabytes input: standard input output: standard output Let $ D(x) $ be the number of positive divisors of a positive integer $ x $ . For example, $

【Educational Codeforces Round 37】F. SUM and REPLACE 线段树+线性筛

题意 给定序列$a_n$,每次将$[L,R]$区间内的数$a_i$替换为$d(a_i)$,或者询问区间和 这题和区间开方有相同的操作 对于$a_i \in (1,10^6)$,$10$次$d(a_i)$以内肯定可以最终化为$1$或者$2$,所以线段树记录区间最大值和区间和,$Max\le2$就返回,单点暴力更新,最后线性筛预处理出$d$ 时间复杂度$O(m\log n)$ 代码 #include <bits/stdc++.h> using namespace std; typedef long

Codeforces 85D Sum of Medians(线段树)

85D Sum of Medians 题目链接 题意:一个集合有添加,删除元素,每次查询输出集合位置为i % 5 == 3的位置和 思路:线段树,线段树记录下% 5 == 0, 1, 2, 3, 4的和,并且记录一个mov表示右移多少,每次添加一个值的时候,就当前位置之后的一整段位置都要右移一个单位,这样去搞线段树维护一下即可 代码: #include <cstdio> #include <cstring> #include <cstdlib> #include <

Codeforces 920F. SUM and REPLACE

题目大意: 一个数列 支持两种操作 1 把区间内的数变成他们自己的约数个数 2 求区间和 思路: 可以想到每个数最终都会变成2或1 然后我们可以线段树 修改的时候记录一下每段有没有全被修改成1或2 是的话就不修改了 不是就暴力修改 因为每个数被修改的次数很小 1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstdlib> 5 #include<cstring&g

Codeforces 444C DZY Loves Colors(线段树)

题目大意:Codeforces 444C DZY Loves Colors 题目大意:两种操作,1是修改区间上l到r上面德值为x,2是询问l到r区间总的修改值. 解题思路:线段树模板题. #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> using namespace std; const int maxn = 5*1e5; typedef long lo

Codeforces 57B Martian Architecture 暴力||线段树

题目链接:点击打开链接 题意:n长的序列(初始全为0) m个操作 k个查询 下面m个操作[l,r] h 代表 a[l] +=h; a[l+1] += h+i; a[l+i] += h+i;  l<=i<=r 然后问k个位置的和 因为k<=100 所以直接暴力也可以 ----------------------- 如果k<=100000 也是可以做的 只需要给区间记录一个标记lazy,表示从左端点开始 l, l+1, l+i ··· l+r 而向下更新时, 左区间则直接更新, 右区间

Codefroces 920F SUM and REPLACE(线段树)

SUM and REPLACE 题意:给你n个数,进行m次操作,分别是将区间[l,r]内的所有数替换成自己的因子数 和 对区间[l,r]进行求和. 题解:可以发现2的因子个数还是2,1的因子个数还是1,所以如果某个数被更新成1或者2之后就不需要再进行更新了. 1 #include<bits/stdc++.h> 2 #define ll long long 3 #define lson l,m,rt<<1 4 #define rson m+1,r,rt<<1|1 5 us

Educational Codeforces Round 72 (Rated for Div. 2)E. Sum Queries?(线段树区间合并)

https://codeforc.es/contest/1217/problem/E 建立9棵数位线段树维护区间最小值和次小值,建议用struct建树方便进行区间合并 1 #define bug(x) cout<<#x<<" is "<<x<<endl 2 #define IO std::ios::sync_with_stdio(0) 3 #include <bits/stdc++.h> 4 #define iter ::it

Codeforces GYM 100114 D. Selection 线段树维护DP

D. Selection Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100114 Description When selecting files in an application dialog, Vasya noted that he can get the same selection in different ways. A simple mouse click selects a sing