HDU 4027 Can you answer these queries?

http://acm.hdu.edu.cn/showproblem.php?pid=4027

题目

给出一串数字,表示一排战舰的耐久度= =

有两种操作

  1. 使用武器,把从x到y的每一个战舰的耐久度开平方,向下取整
  2. 查询从x到y的战舰的耐久度的和,保证和不会超过$2^{63}$

100000个战舰,100000操作,2s时限

题解

开根号没有什么好的区间性质,看不到什么好的线段树的特点……

不过开根号开到几次再开就不会变了,可以用这个性质设定一个区间开关之类的东西,到不会变的区间就不再修改

这样最多执行6*100000次开方,可以卡进时限= =

 

另外线段树在递归的时候是数学归纳,递归的区间一定是与操作的区间相交的,于是要保证下一层也相交,只用考虑递归的区间的中点与查询区间中点的关系

因为相交,所以不存在红框的情况,所以判断左子区间是否与黄框相交就可以了,还要注意右区间是[m+1,r]

 

由于是从x到y,所以不能把l,r直接和x,y对应,还需要考虑大小

 

AC代码

#include<bits/stdc++.h>
using namespace std;

#define REP(r,x,y) for(int r=(x); r<y; r++)
#define REPE(r,x,y) for(int r=(x); r<=y; r++)
#define PERE(r,x,y) for(int r=(x); r>=y; r++)

typedef long long LL;

#define MAXN 100007
struct node {
    int l,r;
    LL v;
    int d;
}t[MAXN<<2];
int n,m;
LL e[MAXN];

void build(int p, int l, int r) {
    node &now = t[p];
    now.l=l, now.r=r;
    now.d=1;
    if(l==r) {
        now.v=e[l];
        return;
    }
    int m=l+r>>1;
    build(p*2,l,m);
    build(p*2+1,m+1,r);
    now.v=t[p*2].v+t[p*2+1].v;
}

void ope(int p, int l, int r) {
    node &now = t[p];
    if(now.l==now.r) {
        now.v=(int)floor(sqrt(now.v));
        if(now.v<=1) now.d=0;
        return;
    }
    int m=now.l+now.r>>1;
    if(m>=l && t[p*2].d) ope(p*2, l, r);
    if(m+1<=r && t[p*2+1].d) ope(p*2+1, l, r);

    now.d= t[p*2].d || t[p*2+1].d;
    now.v=t[p*2].v+t[p*2+1].v;
}

LL opq(int p, int l, int r) {
    node &now = t[p];
    if(now.l>=l && now.r<=r) {
        return now.v;
    }
    int m=(now.l+now.r)>>1;
    LL ans=0;
    if(m>=l) ans+=opq(p*2, l, r);
    if(m+1<=r) ans+=opq(p*2+1,l,r);
    return ans;
}

int main() {
    #ifdef sahdsg
    freopen("in.txt", "r", stdin);
    #endif // sahdsg
    int kase=0;
    while(~scanf("%d", &n)) {
        printf("Case #%d:\n", ++kase);
        REPE(i,1,n) {
            scanf("%lld", &e[i]);
        }
        build(1,1,n);
        scanf("%d", &m);
        REP(i,0,m) {
            int T,x,y;
            scanf("%d%d%d", &T, &x, &y);
            if(x>y) swap(x,y);
            if(T==0) ope(1,x,y);
            else {
                printf("%lld\n", opq(1,x,y));
            }
        }
        putchar('\n');
    }

    return 0;
}

 

转载于:https://www.cnblogs.com/sahdsg/p/10985179.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值