bzoj3211: 花神游历各国

链接

  http://www.lydsy.com/JudgeOnline/problem.php?id=3211

题解

  容易发现, 109 开6平方根就变成1了,所以可以每个线段树开一个 w 数组,wi表示这个区间整体开 i 次根号的和是多少,tag表示这个区间被整体开根了多少次。
  更新的时候直接枚举左右儿子的 wi 然后加起来就好了,下放标记的时候让 wi 整体滚动就行了。
  这道题加了数据之后卡常数,它本意可能是想卡分块…但是线段树常数大的话也可能卡掉(我就被卡掉了)。在下放标记和用儿子更新自己的时候,如果发现 w1=w2 ,那说明所有数都已经开根到极限了,这时候可以直接退出。
  然后就过了。

代码

//线段树
#include <cstdio>
#include <algorithm>
#include <cmath>
#define maxn 100010
#define ll long long
using namespace std;
ll N, M, ndtot, a[maxn], building=1;
struct segtree
{
    ll l, r, w[7], tag;
    segtree *ch[2], *f;
}pool[maxn*4], *root;
inline ll read(ll x=0)
{
    char c=getchar();
    while(c<48 or c>57)c=getchar();
    while(c>=48 and c<=57)x=(x<<1)+(x<<3)+c-48,c=getchar();
    return x;
}
inline void pushdown(segtree *p)
{
    if(p->tag)
    {
        if(p->w[1]==p->w[2]){p->tag=0;return;}
        ll i;
        for(i=1;i<=6;i++)p->w[i]=p->w[min(6ll,i+p->tag)];
        if(p->ch[0])p->ch[0]->tag+=p->tag, p->ch[1]->tag+=p->tag;
        p->tag=0;
    }
}
inline void pushup(segtree *p)
{
    if(p->l==p->r)return;
    ll i;
    pushdown(p->ch[0]), pushdown(p->ch[1]);
    if(!building and p->w[1]==p->w[2])return;
    for(i=1;i<=6;i++)p->w[i]=p->ch[0]->w[i]+p->ch[1]->w[i];
}
void segtag(segtree *p, ll l, ll r)
{
    pushdown(p);
    ll mid=(p->l+p->r)>>1;
    if(l<=p->l and r>=p->r){p->tag++;return;}
    if(l<=mid)segtag(p->ch[0],l,r);
    if(r>mid)segtag(p->ch[1],l,r);
    pushup(p);
}
ll segsum(segtree *p, ll l, ll r)
{
    pushdown(p);
    ll mid=(p->l+p->r)>>1, ans=0;
    if(l<=p->l and r>=p->r)return p->w[1];
    if(l<=mid)ans+=segsum(p->ch[0],l,r);
    if(r>mid)ans+=segsum(p->ch[1],l,r);
    return ans;
}
void build(segtree *p, ll l, ll r)
{
    ll mid=(l+r)>>1, i;
    p->l=l, p->r=r;
    if(l==r)
    {
        p->w[1]=a[l];
        for(i=2;i<=6;i++)p->w[i]=sqrt(p->w[i-1]);
        return;
    }
    build(p->ch[0]=pool+ ++ndtot,l,mid);
    build(p->ch[1]=pool+ ++ndtot,mid+1,r);
    pushup(p);
}
int main()
{
    ll x, l, r, i;
    N=read();
    for(i=1;i<=N;i++)a[i]=read();
    build(root=pool+ ++ndtot,1,N);
    building=0;
    M=read();
    for(i=1;i<=M;i++)
    {
        x=read(), l=read(), r=read();
        if(x==1)printf("%lld\n",segsum(root,l,r));
        if(x==2)segtag(root,l,r);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值