别做(第一次选拔赛)
作者:互联网
将近一年了,又看到这道题,想起来自己在第一次选拔赛是的惊慌失措,堪比坐牢
然而,在做这道题时,我只知道它是一个线段树,是一个不用懒标记的线段树,知道我翻看了题解后才得知它的作法
对于一个数量级为\(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