标签:
1
5 5
1 2 3 4 5
1 3 5 2
2 1 4
3 2 4
2 3 5
3 1 5
5
6
题目大意:
区间三种操作。
1 区间加
2 区间开根
3 区间求和
第一个和第三个用Lazy外加合并相同区间。时间很充裕
开根和区间加不一样的是与元素本身有关。所以很爆炸的要找到每一个叶子。
如果单单这样其实最差的复杂度只会在构造出这种序列后的第一次开根。
但是有一种特殊情况,类似
这样如果序列是交错排列的2 3 2 3 2 3 2 3,然后对区间不断+6 开根 +6 开根,就退化到了单次
这组数据是在赛后加的,当时一片片TLE……现在一瞅过了不少,然后百度了一下发现有人发了更优美的题解,然后就来补了一下。
开根的处理就是除了维护区间和外,再维护一个区间最大mx与最小mn。
当区间mx-mn <= 1 && sqrt(mx)-sqrt(mn) <= 1,就可以cut了,因为开根前后差值是一样的,就变为了区间减法,lazy标记一下就可以了
另发现绝版输入挂,常数优化绝妙
代码如下:
#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <climits>
#include <ctime>
#include <cstring>
#include <queue>
#include <stack>
#include <list>
#include <algorithm>
#include <map>
#include <set>
#define LL long long
#define Pr pair<int,int>
#define frd(ch) freopen(ch,"r",stdin)
#define fwrite(ch) freopen(ch,"w",stdout)
using namespace std;
const int INF = 0x3f3f3f3f;
const int msz = 112345;
const int mod = 1e9+7;
const double eps = 1e-8;
const int BufferSize=1<<16;
char buffer[BufferSize],*head,*tail;
inline char Getchar() {
if(head==tail) {
int l=fread(buffer,1,BufferSize,stdin);
tail=(head=buffer)+l;
}
return *head++;
}
inline int read() {
int x=0,f=1;char c=Getchar();
for(;!isdigit(c);c=Getchar()) if(c==‘-‘) f=-1;
for(;isdigit(c);c=Getchar()) x=x*10+c-‘0‘;
return x*f;
}
LL sum[4*msz],mx[4*msz],mn[4*msz],lz[4*msz];
void down(int root,int l,int r)
{
if(!lz[root]) return;
int mid = (l+r)>>1;
mx[root<<1] += lz[root];
mx[root<<1|1] += lz[root];
mn[root<<1] += lz[root];
mn[root<<1|1] += lz[root];
sum[root<<1] += lz[root]*(mid-l+1);
sum[root<<1|1] += lz[root]*(r-mid);
lz[root<<1] += lz[root];
lz[root<<1|1] += lz[root];
lz[root] = 0;
}
void up(int root)
{
sum[root] = sum[root<<1]+sum[root<<1|1];
mx[root] = max(mx[root<<1],mx[root<<1|1]);
mn[root] = min(mn[root<<1],mn[root<<1|1]);
}
void Add(int root,int l,int r,int ll,int rr,LL ad)
{
if(l == ll && r == rr)
{
lz[root] += ad;
sum[root] += ad*(r-l+1);
mx[root] += ad;
mn[root] += ad;
return;
}
int mid = (l+r)>>1;
down(root,l,r);
if(mid >= rr) Add(root<<1,l,mid,ll,rr,ad);
else if(mid+1 <= ll) Add(root<<1|1,mid+1,r,ll,rr,ad);
else
{
Add(root<<1,l,mid,ll,mid,ad);
Add(root<<1|1,mid+1,r,mid+1,rr,ad);
}
up(root);
}
LL Sum(int root,int l,int r,int ll,int rr)
{
if(l == ll && r == rr)
{
return sum[root];
}
int mid = (l+r)>>1;
down(root,l,r);
if(mid >= rr) return Sum(root<<1,l,mid,ll,rr);
else if(mid+1 <= ll) return Sum(root<<1|1,mid+1,r,ll,rr);
return Sum(root<<1,l,mid,ll,mid)+Sum(root<<1|1,mid+1,r,mid+1,rr);
}
void Sqrt(int root,int l,int r,int ll,int rr)
{
//printf("[%d,%d] mx%lld mn%lld\n",l,r,mx[root],mn[root]);
if(l == ll && r == rr)
{
LL cut;
if(mx[root] == mn[root])
{
cut = mx[root]-floor(sqrt(mx[root]*1.0));
lz[root] -= cut;
sum[root] -= cut*(r-l+1);
mx[root] = mn[root] = mx[root]-cut;
return;
}
else if(mx[root]-mn[root] == 1)
{
LL t1,t2;
t1 = floor(sqrt(mx[root]*1.0));
t2 = floor(sqrt(mn[root]*1.0));
if(t1-t2 == 1)
{
cut = mx[root]-t1;
lz[root] -= cut;
sum[root] -= cut*(r-l+1);
mx[root] = t1;
mn[root] = t2;
return;
}
}
}
int mid = (l+r)>>1;
down(root,l,r);
if(mid >= rr) Sqrt(root<<1,l,mid,ll,rr);
else if(mid+1 <= ll) Sqrt(root<<1|1,mid+1,r,ll,rr);
else
{
Sqrt(root<<1,l,mid,ll,mid);
Sqrt(root<<1|1,mid+1,r,mid+1,rr);
}
up(root);
}
void init(int root,int l,int r)
{
lz[root] = 0;
if(l == r)
{
sum[root] = read();
mx[root] = mn[root] = sum[root];
return;
}
int mid = (l+r)>>1;
init(root<<1,l,mid);
init(root<<1|1,mid+1,r);
up(root);
}
int main()
{
//frd("1008.in");
//fwrite("m.out");
int t,opt,l,r,x,n,q;
t = read();
while(t--)
{
n = read();
q = read();
init(1,1,n);
while(q--)
{
opt = read();
l = read();
r = read();
if(opt == 1)
{
x = read();
Add(1,1,n,l,r,x);
}
else if(opt == 2)
{
Sqrt(1,1,n,l,r);
}
else printf("%lld\n",Sum(1,1,n,l,r));
}
}
return 0;
}
【HDU 5828】Rikka with Sequence(线段树)
标签:
原文地址:http://blog.csdn.net/challengerrumble/article/details/52273901