说实话,这题确挺坑的.
为什么更新查询时的x, y可以x > y.题意不是说 between the X-th and Y-th battleship 这句话难道没有默认x <= y?
题解:
op == 0 时 将 [x,y] 中所有的数开平方. 这个操作对于线段树来看似无能为力,因为不能使用lz[]数组延迟标记.
所以更新的话必须更新到叶节点,对于每一个叶节点进行更新.这样的话每次更新的时间复杂度是O(n)
显然会TLE
但是需要注意到的是其中的数最大就是2^63. 而2^63最多经过7次开方后就会变成1,之后就不会变了.
思路1:
当更新操作时,[l,r]区间的和为 l - r + 1 时,表示该区间的值都为1,这个时候就不需要更新叶节点了.
可是这样存在一个问题,当初始时有0的时候,就不能仅仅依靠[l,r]区间的和为l-r+1来判断.
但是这样依旧能过,也许数据中没有为0的情况吧
代码:
#include
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
ll arr[maxn];
struct SegTree
{
ll sum[maxn<<2];
void init() {
memset(sum,0,sizeof(sum));
}
void push_up(int rt) {
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void build(ll arr[],int l,int r,int rt) {
if(l == r) {
sum[rt] = arr[l];
return ;
}
int mid = (l + r) >> 1;
build(arr,l,mid,rt<<1);
build(arr,mid+1,r,rt<<1|1);
push_up(rt);
}
void updates(int l,int r,int rt) {
if(sum[rt] == (r-l+1)) return;
if(l == r) {
sum[rt] = ll(sqrt(sum[rt]));
return ;
}
int mid = (l+r)>>1;
updates(l,mid,rt<<1);
updates(mid+1,r,rt<<1|1);
push_up(rt);
}
void update(int ql,int qr,int l,int r,int rt) {
if(ql == l && qr == r) {
if(sum[rt] != (r-l+1)) updates(l,r,rt);
return;
}
int mid = (l + r)>>1;
if(qr <= mid) update(ql,qr,l,mid,rt<<1);
else if(ql > mid) update(ql,qr,mid+1,r,rt<<1|1);
else {
update(ql,mid,l,mid,rt<<1);
update(mid+1,qr,mid+1,r,rt<<1|1);
}
push_up(rt);
}
ll query(int ql,int qr,int l,int r,int rt) {
if(ql == l && qr == r) {
return sum[rt];
}
int mid = (l + r) >> 1;
if(qr <= mid) return query(ql,qr,l,mid,rt<<1);
if(ql > mid) return query(ql,qr,mid+1,r,rt<<1|1);
return query(ql,mid,l,mid,rt<<1) + query(mid+1,qr,mid+1,r,rt<<1|1);
}
}seg;
int main()
{
int n,cas = 0;
while(~scanf("%d",&n))
{
printf("Case #%d:\n",++cas);
for(int i=1;i<=n;i++) scanf("%lld",&arr[i]);
seg.build(arr,1,n,1);
int q;scanf("%d",&q);
while(q--) {
int op,l,r;scanf("%d%d%d",&op,&l,&r);
if(l > r) l^=r,r^=l,l^=r;
if(op)
printf("%lld\n",seg.query(l,r,1,n,1));
else
seg.update(l,r,1,n,1);
}
printf("\n");
}
return 0;
}
思路2:
根据其中的数最多经过7次开方变成1后就不会再变的性质.我们只需要用一个数组记录下每个节点被更新多少次,超过7次后我们就不需要向下更新了.显然这是最正确的做法
#include
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
ll arr[maxn];
struct SegTree
{
ll sum[maxn<<2],lz[maxn<<2];
void init() {
memset(sum,0,sizeof(sum));
memset(lz,0,sizeof(lz));
}
void push_up(int rt) {
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void build(ll arr[],int l,int r,int rt) {
if(l == r) {
sum[rt] = arr[l];
lz[rt] = 0;
return ;
}
int mid = (l + r) >> 1;
build(arr,l,mid,rt<<1);
build(arr,mid+1,r,rt<<1|1);
push_up(rt);
}
void updates(int l,int r,int rt) {
if(l == r) {
sum[rt] = ll(sqrt(sum[rt]));
return ;
}
int mid = (l+r)>>1;
updates(l,mid,rt<<1);
updates(mid+1,r,rt<<1|1);
push_up(rt);
}
void update(int ql,int qr,int l,int r,int rt) {
if(ql == l && qr == r) {
if(lz[rt] <= 7) updates(l,r,rt),lz[rt]++;
return;
}
int mid = (l + r)>>1;
if(qr <= mid) update(ql,qr,l,mid,rt<<1);
else if(ql > mid) update(ql,qr,mid+1,r,rt<<1|1);
else {
update(ql,mid,l,mid,rt<<1);
update(mid+1,qr,mid+1,r,rt<<1|1);
}
push_up(rt);
}
ll query(int ql,int qr,int l,int r,int rt) {
if(ql == l && qr == r) {
return sum[rt];
}
int mid = (l + r) >> 1;
if(qr <= mid) return query(ql,qr,l,mid,rt<<1);
if(ql > mid) return query(ql,qr,mid+1,r,rt<<1|1);
return query(ql,mid,l,mid,rt<<1) + query(mid+1,qr,mid+1,r,rt<<1|1);
}
}seg;
int main()
{
int n,cas = 0;
while(~scanf("%d",&n))
{
printf("Case #%d:\n",++cas);
for(int i=1;i<=n;i++) scanf("%lld",&arr[i]);
seg.init();
seg.build(arr,1,n,1);
int q;scanf("%d",&q);
while(q--) {
int op,l,r;scanf("%d%d%d",&op,&l,&r);
if(l > r) l^=r,r^=l,l^=r;
if(op)
printf("%lld\n",seg.query(l,r,1,n,1));
else
seg.update(l,r,1,n,1);
}
printf("\n");
}
return 0;
}