bzoj3730: 震波(点分树)

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

 

点分树内对每个节点动态维护2颗线段树

线段树以距离为下标,城市的价值为权值

对于节点x的两棵线段树:

一棵维护 点分树中,x的子树 的贡献

一棵维护 点分树中,x对x的父节点的贡献

查询和修改时,暴力往上爬点分树

点分树保证了最多往上爬log次

 

查询x k时,先加上点分树内,x的子树中距离<=k的权值和,

再爬到x的父节点f,若x和f的距离为d,则加上f的子树中距离<=k-d的权值和,还要减去 x对f 贡献的<=k-d的权值和,因为这一部分在之前x的子树中算过了

以此类推 ,这就是第二棵线段树的作用

 

常数优化:

原代码总耗时:20.105 s

#include<cstdio>
#include<iostream>
#include<algorithm>
  
using namespace std;
  
#define N 100001
  
int n,a[N];
  
int front[N],nxt[N<<1],to[N<<1],tot;
  
int all,root;
int siz[N],f[N];
  
bool vis[N];
  
int dep[N];
int fa[N][18],dis[N][18];
  
struct Segment
{
    int cnt;
    int rt[N];
    int lc[N*80],rc[N*80],val[N*80];
      
    void Change(int &k,int l,int r,int x,int y)
    {
        if(!k) k=++cnt;
        if(l==r)
        {
            val[k]+=y;
            return;
        }
        int mid=l+r>>1;
        if(x<=mid) Change(lc[k],l,mid,x,y);
        else Change(rc[k],mid+1,r,x,y);
        val[k]=val[lc[k]]+val[rc[k]];
    }
      
    int Query(int k,int l,int r,int x)
    {
        if(!k) return 0;
        if(r<=x) return val[k];
        int mid=l+r>>1;
        if(x<=mid) return Query(lc[k],l,mid,x);
        else return val[lc[k]]+Query(rc[k],mid+1,r,x);
    }
      
}tr,ftr;
  
void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
}
  
void add(int u,int v)
{
    to[++tot]=v; nxt[tot]=front[u]; front[u]=tot;
    to[++tot]=u; nxt[tot]=front[v]; front[v]=tot;
}
  
void getroot(int x,int y)
{
    siz[x]=1; f[x]=0;
    for(int i=front[x];i;i=nxt[i])
        if(to[i]!=y && !vis[to[i]]) 
        {
            getroot(to[i],x);
            siz[x]+=siz[to[i]];
            f[x]=max(f[x],siz[to[i]]);
        }
    f[x]=max(f[x],all-siz[x]);
    if(f[x]<f[root]) root=x;
}
  
void cal(int x,int ancestor,int father,int d)
{
    int t;
    for(int i=front[x];i;i=nxt[i])
    {
        t=to[i];
        if(t!=father && !vis[t])
        {
            fa[t][++dep[t]]=ancestor;
            dis[t][dep[t]]=d+1;
            cal(t,ancestor,x,d+1);
        }
    }   
}
  
void build(int x)
{
    vis[x]=true;
    cal(x,x,0,0);
    int tmp=all;
    for(int i=front[x];i;i=nxt[i])
        if(!vis[to[i]])
        {
            all=siz[to[i]];
            if(all>siz[x]) all=tmp-siz[x];
            root=0;
            getroot(to[i],0);
            build(root);
        }
}
  
void change(int x,int y)
{
    tr.Change(tr.rt[x],0,n-1,0,y);
    int d;
    for(int i=dep[x];i;--i)
    {
        ftr.Change(ftr.rt[fa[x][i+1]],0,n-1,dis[x][i],y);
    //  printf("kk %d\n",dis[x][i]);
        tr.Change(tr.rt[fa[x][i]],0,n-1,dis[x][i],y);
    }
}
  
int query(int x,int d)
{
    int ans=tr.Query(tr.rt[x],0,n-1,d);
    for(int i=dep[x];i;--i)
    {
        if(d-dis[x][i]>=0) ans+=tr.Query(tr.rt[fa[x][i]],0,n-1,d-dis[x][i]);
        if(d-dis[x][i]>=0) ans-=ftr.Query(ftr.rt[fa[x][i+1]],0,n-1,d-dis[x][i]);
    }
    return ans;
}
  
void out(int x)
{
    if(x>=10) out(x/10);
    putchar(x%10+'0');
}
  
int main()
{
    freopen("wave.in","r",stdin);
    freopen("wave.out","w",stdout); 
      int size = 256 << 20; // 256MB  
    char *p = (char*)malloc(size) + size;  
    __asm__("movl %0, %%esp\n" :: "r"(p));
    int m;
    read(n); read(m);
    for(int i=1;i<=n;++i) read(a[i]);
    int u,v;
    for(int i=1;i<n;++i)
    {
        read(u); read(v);
        add(u,v);
    }
    f[0]=n+1;
    all=n;
    getroot(1,0);
    build(root);
    for(int i=1;i<=n;++i) fa[i][dep[i]+1]=i;
    for(int i=1;i<=n;++i) 
    change(i,a[i]);
    int ty,last=0;
    while(m--)
    {
        read(ty); read(u); read(v);
        u^=last; v^=last;
        if(!ty) last=query(u,v),out(last),printf("\n");
        else change(u,v-a[u]),a[u]=v;
    }
    //printf("%d %d",tr.cnt,ftr.cnt);
    return 0;
}
原代码

1、原本线段树的操作封装在结构体里,拿出来,总耗时:16.934 s

2、动态开节点线段树 单点加:

在寻找x的路径上就进行加操作,而不是找到后再update

总耗时:14.357 s

3、数组改成结构体 总耗时:12.684 s

 

#include<cstdio>
#include<iostream>
#include<algorithm>
   
using namespace std;
   
#define N 100001
   
int n,a[N];
   
int front[N],nxt[N<<1],to[N<<1],tot;
   
int all,root;
int siz[N],f[N];
   
bool vis[N];
   
int dep[N];
int fa[N][18],dis[N][18];
 
int rt[N<<1],cnt;
 
struct node
{
    int lc,rc,val;
}tr[N*150];
 
    void Change(int &k,int l,int r,int x,int y)
    {
        if(!k) k=++cnt;
        tr[k].val+=y;
        if(l==r) return;
        int mid=l+r>>1;
        if(x<=mid) Change(tr[k].lc,l,mid,x,y);
        else Change(tr[k].rc,mid+1,r,x,y);
         
    }
       
    int Query(int k,int l,int r,int x)
    {
        if(!k) return 0;
        if(r<=x) return tr[k].val;
        int mid=l+r>>1;
        if(x<=mid) return Query(tr[k].lc,l,mid,x);
        else return tr[tr[k].lc].val+Query(tr[k].rc,mid+1,r,x);
    }
   
void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
}
   
void add(int u,int v)
{
    to[++tot]=v; nxt[tot]=front[u]; front[u]=tot;
    to[++tot]=u; nxt[tot]=front[v]; front[v]=tot;
}
   
void getroot(int x,int y)
{
    siz[x]=1; f[x]=0;
    for(int i=front[x];i;i=nxt[i])
        if(to[i]!=y && !vis[to[i]]) 
        {
            getroot(to[i],x);
            siz[x]+=siz[to[i]];
            f[x]=max(f[x],siz[to[i]]);
        }
    f[x]=max(f[x],all-siz[x]);
    if(f[x]<f[root]) root=x;
}
   
void cal(int x,int ancestor,int father,int d)
{
    int t;
    for(int i=front[x];i;i=nxt[i])
    {
        t=to[i];
        if(t!=father && !vis[t])
        {
            fa[t][++dep[t]]=ancestor;
            dis[t][dep[t]]=d+1;
            cal(t,ancestor,x,d+1);
        }
    }   
}
   
void build(int x)
{
    vis[x]=true;
    cal(x,x,0,0);
    int tmp=all;
    for(int i=front[x];i;i=nxt[i])
        if(!vis[to[i]])
        {
            all=siz[to[i]];
            if(all>siz[x]) all=tmp-siz[x];
            root=0;
            getroot(to[i],0);
            build(root);
        }
}
   
void change(int x,int y)
{
    Change(rt[x],0,n-1,0,y);
    int d;
    for(int i=dep[x];i;--i)
    {
        Change(rt[fa[x][i+1]+n],0,n-1,dis[x][i],y);
    //  printf("kk %d\n",dis[x][i]);
        Change(rt[fa[x][i]],0,n-1,dis[x][i],y);
    }
}
   
int query(int x,int d)
{
    int ans=Query(rt[x],0,n-1,d);
    for(int i=dep[x];i;--i)
    {
        if(d-dis[x][i]>=0) ans+=Query(rt[fa[x][i]],0,n-1,d-dis[x][i]);
        if(d-dis[x][i]>=0) ans-=Query(rt[fa[x][i+1]+n],0,n-1,d-dis[x][i]);
    }
    return ans;
}
   
void out(int x)
{
    if(x>=10) out(x/10);
    putchar(x%10+'0');
}
   
int main()
{
    int m;
    read(n); read(m);
    for(int i=1;i<=n;++i) read(a[i]);
    int u,v;
    for(int i=1;i<n;++i)
    {
        read(u); read(v);
        add(u,v);
    }
    f[0]=n+1;
    all=n;
    getroot(1,0);
    build(root);
    for(int i=1;i<=n;++i) fa[i][dep[i]+1]=i;
    for(int i=1;i<=n;++i) 
    change(i,a[i]);
    int ty,last=0;
    while(m--)
    {
        read(ty); read(u); read(v);
        u^=last; v^=last;
        if(!ty) last=query(u,v),out(last),printf("\n");
        else change(u,v-a[u]),a[u]=v;
    }
    //printf("%d %d",tr.cnt,ftr.cnt);
    return 0;
}

 

posted @ 2018-02-22 14:42  TRTTG  阅读(291)  评论(0编辑  收藏  举报