因数的个数 线段树维护

Let D(x) be the number of positive divisors of a positive integer x. For example, D(2) = 2 (2 is divisible by 1 and 2), D(6) = 4 (6 is divisible by 1, 2, 3 and 6).

You are given an array a of n integers. You have to process two types of queries:

  1. REPLACE l r — for every replace ai with D(ai);
  2. SUM l r — calculate .

Print the answer for each SUM query.

Input

The first line contains two integers n and m (1 ≤ n, m ≤ 3·105) — the number of elements in the array and the number of queries to process, respectively.

The second line contains n integers a1, a2, ..., an (1 ≤ ai ≤ 106) — the elements of the array.

Then m lines follow, each containing 3 integers ti, li, ri denoting i-th query. If ti = 1, then i-th query is REPLACE liri, otherwise it's SUM liri (1 ≤ ti ≤ 2, 1 ≤ li ≤ ri ≤ n).

There is at least one SUM query.

Output

For each SUM query print the answer to it.

Example
Input
7 6
6 4 1 10 3 2 4
2 1 7
2 4 5
1 3 5
2 4 4
1 5 7
2 1 7
Output
30
13
4
22

题目分析 :题目说了一种操作,就是将一个数变成它的因数的个数,第二种操作是查询一段区间的和,首先你观察它的查询和更改的操作次数有 3e5 次,查询的话肯定是没有问题的, logn 的复杂度,但是你更改呢?每个数都改,一定会超时,那么要看下这些被改的数有什么特征,一个很大的数,经过一次质因数操作,会变成一个相对很小的数,重复此过程,会变得越来越小,直到变成2,那么我线段树可以优化的地方是不就在这里,设一个标记,就可以了
代码示例 :
const ll maxn = 1e6+5;
const ll maxn2 = 3e5+5;
const double pi = acos(-1.0);
const ll inf = 0x3f3f3f3f;
#define lson k<<1
#define rson k<<1|1

ll cnt[maxn];

struct node
{
    ll l, r;
    ll sum;
    ll pt;
}t[maxn2<<2];

void init() {
    for(int i = 1; i <= 1000000; i++) cnt[i] = 2;
    cnt[1] = 1;
    for(int i = 2; i <= 1000000; i++){
        for(int j = i+i; j <= 1000000; j += i) cnt[j]++;
    }
}

void pushup(int k){
    t[k].sum = t[lson].sum + t[rson].sum;
    if (t[lson].pt && t[rson].pt) t[k].pt = 1;
}

void build(ll l, ll r, ll k){
    t[k].l = l; t[k].r = r;
    
    t[k].pt = 0;
    if (l == r) {
        scanf("%lld", &t[k].sum);
        if (t[k].sum == cnt[t[k].sum]) t[k].pt = 1;
        return;
    }
    ll m = (l + r) >> 1;
    build(l, m, lson);
    build(m+1, r, rson);
    pushup(k);
}

ll query(ll l, ll r, ll k){
    ll s = 0;
    
    if (l <= t[k].l && t[k].r <= r){
        s += t[k].sum;
        return s;
    }
    ll m = (t[k].l + t[k].r) >> 1;
    if (l <= m) s += query(l, r, lson);
    if (r > m) s += query(l, r, rson);
    return s;
}

void update(ll l, ll r, ll k){
    if (t[k].pt) return;
    if (t[k].l == t[k].r) {
        t[k].sum = cnt[t[k].sum];
        if (t[k].sum == cnt[t[k].sum]) t[k].pt = 1;
        //printf("***** %lld\n", cnt[x]);
        return;
    }
    ll m = (t[k].l + t[k].r) >> 1;
    if (l <= m) update(l, r, lson);
    if (r > m) update(l, r, rson);
    pushup(k);
}

int main() {
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    ll n, m;
    ll pt, l, r;
    
    init();
    //for(ll i = 1; i <= 100; i++) printf("%lld  %lld\n", i, cnt[i]);
    cin >> n >> m;
    build(1, n, 1);
    //printf("%lld\n", query(1, 7, 1));
    for(ll i = 1; i <= m; i++){
        scanf("%lld%lld%lld", &pt, &l, &r);
        if (pt == 1) {
            update(l, r, 1);
        }
        else {
            printf("%lld\n", query(l, r, 1));
        }    
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/ccut-ry/p/8410489.html

# CF2126G2 Big Wins! (hard version) ## 题目描述 这是该问题的困难版本。不同之处在于本版本中 $a_i \leq n$。 给定一个长度为 $n$ 的整数数组 $a_1, a_2, \dots, a_n$。 你的任务是找到一个子数组 $a[l, r]$(即一段连续的元素 $a_l, a_{l + 1}, \dots, a_r$),使得表达式 $\text{med}(a[l, r]) - \min(a[l, r])$ 的值最大。 其中: - $\text{med}$ 表示子数组的中位数,即将子数组排序后,第 $\left\lceil \frac{k + 1}{2} \right\rceil$ 个元素,$k$ 为子数组长度; - $\min$ 表示该子数组中的最小元素。 例如,考虑数组 $a=[1, 4, 1, 5, 3, 3]$,选择子数组 $a[2, 5] = [4, 1, 5, 3]$。排序后为 $[1, 3, 4, 5]$。 - $\text{med}(a[2, 5]) = 4$,因为 $\left\lceil \frac{4 + 1}{2} \right\rceil = 3$,排序后第 3 个元素为 $4$; - $\min(a[2, 5]) = 1$,因为最小元素为 $1$。 在本例中,$\text{med} - \min = 4 - 1 = 3$。 ## 输入格式 第一行包含一个整数 $t$($1 \le t \le 10^4$),表示测试用例的数量。 每个测试用例的第一行包含一个整数 $n$($1 \leq n \leq 2 \cdot 10^5$),表示数组的长度。 每个测试用例的第二行包含 $n$ 个整数 $a_1, a_2, \dots, a_n$($1 \leq a_i \leq n$),表示数组的元素。 保证所有测试用例中 $n$ 的总和不超过 $2 \cdot 10^5$。 ## 输出格式 对于每个测试用例,输出一个整数,表示所有子数组中 $\text{med} - \min$ 的最大可能值。 ## 输入输出样例 #1 ### 输入 #1 ``` 5 5 3 2 5 3 1 4 4 1 1 3 7 6 1 3 4 6 2 7 4 4 2 3 1 5 1 2 3 4 5 ``` ### 输出 #1 ``` 3 3 5 2 2 ``` ## 说明/提示 在第一个示例中,考虑数组 $a=[3,\ 2,\ 5,\ 3,\ 1]$,可以选择子数组 $a[2,\ 3]$,即元素 $[2,\ 5]$。 - 子数组长度为 $2$。 - 中位数为排序后第 $\left\lceil \dfrac{3}{2} \right\rceil = 2$ 个元素。排序后为 $[2,\ 5]$,$\text{med} = 5$。 - 子数组的最小元素为 $2$。 因此,$\text{med} - \min = 5 - 2 = 3$,这是最大答案。 在第二个测试用例中,数组 $a=[4,\ 1,\ 1,\ 3]$,可以选择子数组 $a[1,\ 2]$,即元素 $[4,\ 1]$。 - 子数组长度为 $2$。 - 中位数为排序后第 $\left\lceil \dfrac{3}{2} \right\rceil = 2$ 个元素。排序后为 $[1,\ 4]$,$\text{med} = 4$。 - 子数组的最小元素为 $1$。 因此,$\text{med} - \min = 4 - 1 = 3$。 可以证明,这两个子数组都是最优的,能够得到表达式 $\text{med} - \min$ 的最大值。 由 ChatGPT 4.1 翻译 //https://blog.youkuaiyun.com/ez_gsn/article/details/124840464?ops_request_misc=elastic_search_misc&request_id=743433b74cd2da2925edc4e1cc7af925&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~ElasticSearch~search_v2-1-124840464-null-null.142^v102^pc_search_result_base9&utm_term=P2839%20%5B%E5%9B%BD%E5%AE%B6%E9%9B%86%E8%AE%AD%E9%98%9F%5D%20middle #include<bits/stdc++.h> #define int long long using namespace std; int n; int a[20005]; int b[20005]; int sl; vector<int> pos[20005]; int cnt; int rt[20005]; struct TREE { int l,r; int sum;//区间和 int lml,rml;//最大前缀和,最大后缀和 } tr[20005*40]; void pushup(int x) { tr[x].sum=tr[tr[x].l].sum+tr[tr[x].r].sum; tr[x].lml=max(tr[tr[x].l].lml,tr[tr[x].l].sum+tr[tr[x].r].lml); tr[x].rml=max(tr[tr[x].r].rml,tr[tr[x].r].sum+tr[tr[x].l].rml); } int build(int i,int l,int r) { i=++cnt; if(l==r) { tr[i].sum=tr[i].lml=tr[i].rml=1; //初始线段树对应最小中位数候选值(mid = num[1]), //此时所有元素都 ≥ mid,因此每个位置都是 1。 return i; } int mid=(l+r)/2; tr[i].l=build(tr[i].l,l,mid); tr[i].r=build(tr[i].r,mid+1,r); pushup(i); return i; } int update(int x,int y,int ps,int l,int r) { //单点修改 //f:是否需要新建节点 0:需要 1:不需要 //x:当前新版本的节点索引(需修改的节点) //y:旧版本的对应节点索引(数据来源) x=++cnt; if(l==r) { tr[x].lml=tr[x].rml=tr[x].sum=1; return x; } tr[x]=tr[y]; int mid=(l+r)/2; if(ps<=mid) tr[x].l=update(tr[x].l,tr[y].l,ps,l,mid); else tr[x].r=update(tr[x].r,tr[y].r,ps,mid+1,r); pushup(x); return x; } int query_sum(int i,int l,int r,int qry_l,int qry_r) { //区间和 if(i==0) return 0; if(l>=qry_l&&r<=qry_r) return tr[i].sum; int ans=0,mid=(l+r)/2; if(mid>=qry_l) ans+=query_sum(tr[i].l,l,mid,qry_l,qry_r); if(mid<qry_r) ans+=query_sum(tr[i].r,mid+1,r,qry_l,qry_r); return ans; } TREE query_lml(int i,int l,int r,int qry_l,int qry_r) { //在区间 [qry_l, qry_r] 内部,从 qry_l 开始计算的最大前缀和 if(i==0) return {0,0,0,0,0}; if(l>=qry_l&&r<=qry_r) return tr[i]; int mid=(l+r)/2; if(qry_l<=mid&&qry_r>mid) { //qry_r>r 不能去等,保证右子树不为空 TREE ls,rs; ls=query_lml(tr[i].l,l,mid,qry_l,qry_r); rs=query_lml(tr[i].r,mid+1,r,qry_l,qry_r); TREE ans; ans.sum=ls.sum+rs.sum; ans.lml=max(ls.sum+rs.lml,ls.lml); return ans; } if(qry_l<mid) return query_lml(tr[i].l,l,mid,qry_l,qry_r); else return query_lml(tr[i].r,mid+1,r,qry_l,qry_r); } TREE query_rml(int i,int l,int r,int qry_l,int qry_r) { //在区间 [qry_l, qry_r] 内部,从 qry_l 开始计算的最大前缀和 if(i==0) return {0,0,0,0,0}; if(l>=qry_l&&r<=qry_r) return tr[i]; int mid=(l+r)/2; if(qry_l<=mid&&qry_r>mid) { //qry_r>r 不能去等,保证右子树不为空 TREE ls,rs; ls=query_rml(tr[i].l,l,mid,qry_l,qry_r); rs=query_rml(tr[i].r,mid+1,r,qry_l,qry_r); TREE ans; ans.sum=ls.sum+rs.sum; ans.rml=max(rs.sum+ls.rml,rs.rml); return ans; } if(qry_l<mid) return query_rml(tr[i].l,l,mid,qry_l,qry_r); else return query_rml(tr[i].r,mid+1,r,qry_l,qry_r); } bool cmp(int x,int y) { return a[x]>a[y]; } stack<int> s; int f[20005];//表示位置 i 左边第一个小于 a[i] 的元素的位置 int g[20005];//表示位置 i 右边第一个小于 a[i] 的元素的位置。 main() { // ios::sync_with_stdio(0); // cin.tie(0); // cout.tie(0); int T; cin>>T; while(T--) { int n; cin>>n; for(int i=1; i<=n; i++) cin>>a[i],b[i]=i; sort(b+1,b+1+n,cmp); rt[0]=build(rt[0],1,n); for(int i=1; i<=n; i++) rt[i]=update(rt[i],rt[i-1],b[i],1,n); while(!s.empty()) s.pop(); for (int i = 1; i <= n; i++) f[i] = 0, g[i] = n + 1; for (int i = 1; i <= n; i++) { while (s.size() && a[i] < a[s.top()]) g[s.top()] = i, s.pop(); if (s.size()) f[i] = s.top(); s.push(i); } int ans = 0; for (int i = 1; i <= n; i++) { int x = f[i] + 1, y = g[i] - 1; int l = 1, r = n; while (l < r) { int mid = (l + r) >> 1; int X = query_rml(rt[mid], 1, n, x, i).rml; int Y = -query_sum(rt[mid], 1, n, i, i); int Z = query_lml(rt[mid], 1, n, i, y).lml; if (X + Y + Z >= 0) r = mid; else l = mid + 1; } ans = max(ans, a[b[l]] - a[i]); } cout << ans << '\n'; } } 代码问题
最新发布
08-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值