其他分享
首页 > 其他分享> > 别做(第一次选拔赛)

别做(第一次选拔赛)

作者:互联网


将近一年了,又看到这道题,想起来自己在第一次选拔赛是的惊慌失措,堪比坐牢
然而,在做这道题时,我只知道它是一个线段树,是一个不用懒标记的线段树,知道我翻看了题解后才得知它的作法

对于一个数量级为\(e^{12}\)的数来说,将它开方成1只需要6次即可完成,当一个数为1时,对这个数开方是毫无意义的操作,所以我们需要加入一个标记来标记那些等于1的数的位置

#include <iostream>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
struct node
{
    int l, r;
    ll data;
    bool flag;
}t[N << 3];
ll a[N];

inline void push_up(int p)
{
    t[p].data = t[p << 1].data + t[p << 1 | 1].data;
    t[p].flag = t[p << 1].flag & t[p << 1 | 1].flag;
}
inline void build(int l, int r, int p)
{
    t[p].l = l, t[p].r = r;
    if(l == r)
    {
        t[p].data = a[l];
        t[p].flag = ((a[l] == 1ll) ? 1 : 0);//!如果为一那么如何开根号,结果都为一
        return;
    }
    int mid = l + r >> 1;
    build(l, mid, p << 1);
    build(mid + 1, r, p << 1 | 1);
    push_up(p);
}

inline void modify(int l, int r, int p)
{
    if(t[p].flag)
    return;
    if(t[p].l == t[p].r)
    {
        t[p].data = sqrt(t[p].data);
        if(t[p].data == 1)t[p].flag = 1;
        return;
    }
    int mid = t[p].r + t[p].l >> 1;
    if(mid >= l)
    modify(l, r, p << 1);
    if(mid < r)
    modify(l, r, p << 1 | 1);
    push_up(p);
}

inline ll query(int l, int r, int p)
{
    if(t[p].l >= l && t[p].r <= r)
    {
        return t[p].data;
    }
    ll ans = 0;
    int mid = t[p].l + t[p].r >> 1;
    if(mid >= l)
    ans += query(l, r, p << 1);
    if(mid < r)
    ans += query(l, r, p << 1 | 1);
    return ans;
}
signed main()
{
    int n, m;
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= n; i++)
    scanf("%lld", &a[i]);
    build(1, n, 1);
    while(m--)
    {
        int g, w, e;
        scanf("%d %d %d", &g, &w, &e);
        if(w > e)
        swap(w, e);
        if(g == 1)
            modify(w, e, 1);
        else
        printf("%lld\n", query(w, e, 1));
    }
    return 0;
}

标签:标记,int,ll,mid,long,选拔赛,第一次,include
来源: https://www.cnblogs.com/Flying-bullet/p/16364625.html