[BZOJ3589]动态树(树链剖分)

题目:

我是超链接

题解:

不知道被什么奇奇怪怪的东西卡了好久
一眼就是树剖了啊,但是第二问似乎不友善,由于每次选择了某些链,它们重叠的部分是不能计算的,所以情况比较复杂。
其实转换一下就是标记链上的点,并计算被标记的点的点权和

由于区间的标记仅为0/1,所以对某段区间打标记时可以再开一个数组维护和。

将每条链对应的每段区间在线段树中标记上,然后统计线段树中所有被标记的点的权值和,最后删除所有点的标记。

树剖+线段树区间修改区间查询,时间复杂度 O(Qklog2n) O ( Q k l o g 2 n )
至于mod maxlongint,可以使用int自然溢出,最后结果和maxlongint取与。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=200005;
int tot,nxt[N*2],point[N],v[N*2],n,size[N],f[N],h[N],in[N],son[N],top[N],num,out[N],have[N*4],sum[N*4],delta[N*4],bb[N*4];
void addline(int x,int y)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
    ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void dfs_1(int x,int fa)
{
    f[x]=fa; h[x]=h[fa]+1; size[x]=1;int maxx=0;
    for (int i=point[x];i;i=nxt[i])
      if (v[i]!=fa) 
      {
        dfs_1(v[i],x);
        size[x]+=size[v[i]];
        if (maxx<size[v[i]]) maxx=size[v[i]],son[x]=v[i];
      }
}
void dfs_2(int x,int fa)
{
    if (son[fa]!=x) top[x]=x;
    else top[x]=top[fa];
    in[x]=++num;
    if (son[x])
    {
        dfs_2(son[x],x);
        for (int i=point[x];i;i=nxt[i])
          if (v[i]!=fa && v[i]!=son[x]) dfs_2(v[i],x);
    }
    out[x]=num;
}
void updata(int now){sum[now]=sum[now<<1]+sum[now<<1|1];have[now]=have[now<<1]+have[now<<1|1];}
void pushdown(int now,int l,int r,int mid)
{
    if (delta[now])
    {
        delta[now<<1]+=delta[now];
        delta[now<<1|1]+=delta[now];
        sum[now<<1]+=delta[now]*(mid-l+1);
        sum[now<<1|1]+=delta[now]*(r-mid);
        delta[now]=0;
    }
    if (bb[now]!=-1)
    {
        have[now<<1]=bb[now]*sum[now<<1]; have[now<<1|1]=bb[now]*sum[now<<1|1];
        bb[now<<1]=bb[now<<1|1]=bb[now];
        bb[now]=-1;
    }
}
void change(int now,int l,int r,int lrange,int rrange,int v)
{
    if (lrange<=l && rrange>=r) {sum[now]+=v*(r-l+1); delta[now]+=v; return;}
    int mid=(l+r)>>1;pushdown(now,l,r,mid);
    if (lrange<=mid) change(now<<1,l,mid,lrange,rrange,v);
    if (rrange>mid) change(now<<1|1,mid+1,r,lrange,rrange,v);
    updata(now);
}
void bj(int now,int l,int r,int lrange,int rrange,int co)
{
    if (lrange<=l && rrange>=r) {have[now]=sum[now]*co; bb[now]=co; return;}
    int mid=(l+r)>>1;pushdown(now,l,r,mid);
    if (lrange<=mid) bj(now<<1,l,mid,lrange,rrange,co);
    if (rrange>mid) bj(now<<1|1,mid+1,r,lrange,rrange,co);
    updata(now);
}
void Que(int u,int v)
{
    int f1=top[u],f2=top[v];
    while (f1!=f2)
    {
        if (h[f1]<h[f2]) swap(f1,f2),swap(u,v);
        bj(1,1,n,in[f1],in[u],1);
        u=f[f1]; f1=top[u];
    }
    if (in[u]>in[v]) swap(u,v);
    bj(1,1,n,in[u],in[v],1);
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<n;i++)
    {
        int x,y;scanf("%d%d",&x,&y);
        addline(x,y);
    }
    dfs_1(1,0); dfs_2(1,0);
    memset(bb,-1,sizeof(bb));
    int q,k;scanf("%d",&q);
    while(q--)
    {
        int id,x,v,y;scanf("%d",&id);
        if (id==0) scanf("%d%d",&x,&v),change(1,1,n,in[x],out[x],v);
        else
        {
            scanf("%d",&k);
            while (k--) scanf("%d%d",&x,&y),Que(x,y);
            printf("%d\n",have[1]&0x7fffffff);
            bj(1,1,n,1,n,0);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值