1. 程式人生 > >[HAOI2015]樹上操作

[HAOI2015]樹上操作

一個 -s name sam pla etc 答案 pre scanf

[HAOI2015]樹上操作

2017-09-07


Description

有一棵點數為 N 的樹,以點 1 為根,且樹點有邊權。然後有 M 個 操作,分為三種: 操作 1 :把某個節點 x 的點權增加 a 。 操作 2 :把某個節點 x 為根的子樹中所有點的點權都增加 a 。 操作 3 :詢問某個節點 x 到根的路徑中所有點的點權和。

Input

第一行包含兩個整數 N, M 。表示點數和操作數。接下來一行 N 個整數,表示樹中節點的初始權值。接下來 N-1 行每行三個正整數 fr, to , 表示該樹中存在一條邊 (fr, to) 。再接下來 M 行,每行分別表示一次操作。其中 第一個數表示該操作的種類( 1-3 ) ,之後接這個操作的參數( x 或者 x a ) 。

Output

對於每個詢問操作,輸出該詢問的答案。答案之間用換行隔開。


Sample Input

5 5
1 2 3 4 5
1 2
1 4
2 3
2 5
3 3
1 2 1
3 5
2 1 2
3 3

Sample Output

6
9
13

裸的樹鏈剖分.....線段樹維護兩個dfs建樹...mx[i]記錄所到子樹最大的id編號,方便區間加...因為dfs子樹編號都是連續的 lazy維護區間加,單點加就是自己到自己的區間加.沒什麽.開始以為樹鏈剖分好難 技術分享
#include<cstdio>
#include<cstring>
#include<iostream>
#include
<algorithm> #define ll long long const int maxn=100000+999; using namespace std; int read(){ int an=0,f=1;char ch=getchar(); while(!(0<=ch&&ch<=9)){if(ch==-)f=-f;ch=getchar();} while(0<=ch&&ch<=9){an=an*10+(ch-0);ch=getchar();} return f*an; } int v[maxn],cnt,fa[maxn],id,belong[maxn],mx[maxn],f[maxn],pos[maxn],deep[maxn];
int n,m,size[maxn]; ll tag[maxn*4],sum[maxn*4]; bool vis[maxn]; struct sab{ int nex,to; }b[maxn<<1]; struct saber{ ll lazy,sum,l,r; }tr[maxn*4]; void add(int x,int y){ cnt++;b[cnt].nex=f[x]; f[x]=cnt;b[cnt].to=y;} void dfs(int x){ vis[x]=1;size[x]=1; for(int i=f[x];i;i=b[i].nex){ int v=b[i].to; if(!vis[v]){ fa[v]=x; deep[v]=deep[x]+1; dfs(v); size[x]+=size[v]; } } } void dfs2(int x,int chain){ id++;int k=0; mx[x]=pos[x]=id; belong[x]=chain; for(int i=f[x];i;i=b[i].nex){ int v=b[i].to; if(v!=fa[x]&&size[v]>size[k])k=v;} if(k){dfs2(k,chain);mx[x]=max(mx[x],mx[k]);} for(int i=f[x];i;i=b[i].nex){ int v=b[i].to; if(v!=fa[x]&&k!=v)dfs2(v,v); mx[x]=max(mx[x],mx[v]); } } void pushdown(int l,int r,int k) { if(l==r)return; int mid=(l+r)>>1;ll t=tag[k];tag[k]=0; tag[k<<1]+=t;tag[k<<1|1]+=t; sum[k<<1]+=t*(mid-l+1); sum[k<<1|1]+=t*(r-mid); } void add(int k,int l,int r,int x,int y,ll val) { if(tag[k])pushdown(l,r,k); if(l==x&&y==r){tag[k]+=val;sum[k]+=(r-l+1)*val;return;} int mid=(l+r)>>1; if(x<=mid)add(k<<1,l,mid,x,min(mid,y),val); if(y>=mid+1)add(k<<1|1,mid+1,r,max(mid+1,x),y,val); sum[k]=sum[k<<1]+sum[k<<1|1]; } ll ask(int k,int l,int r,int x,int y) { if(tag[k])pushdown(l,r,k); if(l==x&&y==r)return sum[k]; int mid=(l+r)>>1; ll ans=0; if(x<=mid) ans+=ask(k<<1,l,mid,x,min(mid,y)); if(y>=mid+1) ans+=ask(k<<1|1,mid+1,r,max(mid+1,x),y); return ans; } ll QUE(int x){ ll ans=0; while(belong[x]!=1){ ans+=ask(1,1,n,pos[belong[x]],pos[x]); x=fa[belong[x]]; } ans+=ask(1,1,n,1,pos[x]); return ans; } int main(){ n=read();m=read(); for(int i=1;i<=n;i++)v[i]=read(); for(int i=1;i<n;i++){ int x,y; x=read();y=read(); add(x,y);add(y,x); } dfs(1); dfs2(1,1); for(int i=1;i<=n;i++)add(1,1,n,pos[i],pos[i],v[i]); while(m){ m--; int x,y,z; x=read(); if(x==1){ y=read();z=read();//將y節點加z add(1,1,n,pos[y],pos[y],z);} else if(x==2){ y=read();z=read();//將y的子樹包括他本身+z add(1,1,n,pos[y],mx[y],z); } else{ y=read();//現在開始查詢從y到1的所有點權和.... printf("%lld\n",QUE(y)); } } return 0; }
樹上操作[s醬]

by:s_a_b_e_r


樹鏈剖分……

因為是dfs所以每個子樹都是一段連續的區間

第二遍dfs的時候順便記錄一下從這個點到這個子樹結束的區間

然後就可以把子樹加轉換為區間加了

來人,上懶標簽……O(∩_∩)O~

(以及這題可以偷個懶,把單點加寫成長度為1的區間加)

技術分享
#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
const int N=100009;
int n,m,p[N],cnt,inx;
int size[N],dep[N],fa[N],bl[N],pos[N],mx[N];
ll a[N];
struct node{
int l,r;
ll sum,laz;
}tree[N<<2];
struct edge{
int to,nex;
}e[N<<1];
void adde(int u,int v)
{
     ++cnt;
     e[cnt].to=v;
     e[cnt].nex=p[u];
     p[u]=cnt;
}
void dfs1(int u)
{
     size[u]=1;
     for(int i=p[u];i;i=e[i].nex)
     {
       int v=e[i].to;
       if(v==fa[u])continue;
       dep[v]=dep[u]+1;
       fa[v]=u;
       dfs1(v);
       size[u]+=size[v];
     }
}
void dfs2(int u,int chain)
{
     int k=0;++inx;
     pos[u]=mx[u]=inx;
     bl[u]=chain;
     for(int i=p[u];i;i=e[i].nex)
     {
       int v=e[i].to;
       if(dep[u]<dep[v]&&size[k]<size[v])k=v;
     }
     if(k){dfs2(k,chain);mx[u]=max(mx[u],mx[k]);}
     for(int i=p[u];i;i=e[i].nex)
     {
       int v=e[i].to;
       if(dep[u]<dep[v]&&k!=v)
       {dfs2(v,v);mx[u]=max(mx[u],mx[v]);}
     }
}
void build(int k,int l,int r)
{
     tree[k].l=l,tree[k].r=r;
     if(l==r)return;
     int mid=(l+r)>>1;
     build(k<<1,l,mid);
     build(k<<1|1,mid+1,r);
}
void add(int k,int p,ll w)
{
    int l=tree[k].l,r=tree[k].r;
    if(l==r){tree[k].sum+=w;return;}
    int mid=(l+r)>>1;
    if(p<=mid)add(k<<1,p,w);
    else add(k<<1|1,p,w);
    tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
}
void addlaz(int k,ll w)
{
     tree[k].sum+=(tree[k].r-tree[k].l+1)*w;
     tree[k].laz+=w;
}
void pushdown(int k)
{
     addlaz(k<<1,tree[k].laz);
     addlaz(k<<1|1,tree[k].laz);
     tree[k].laz=0;
}
void addall(int k,int L,int R,ll w)
{
     int l=tree[k].l,r=tree[k].r;
     if(r<L||l>R)return;
     if(L<=l&&r<=R)
     {
       tree[k].sum+=(r-l+1)*w;
       tree[k].laz+=w;
       return;
     }
     pushdown(k);
     addall(k<<1,L,R,w);
     addall(k<<1|1,L,R,w);
     tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
}
ll ask(int k,int L,int R)
{
    int l=tree[k].l,r=tree[k].r;
    if(r<L||l>R)return 0;
    if(L<=l&&R>=r)return tree[k].sum;
    pushdown(k);
    return ask(k<<1,L,R)+ask(k<<1|1,L,R);
}
ll query(int x)
{
    ll ans=0;
    while(bl[x]!=1)
    {
      ans+=ask(1,pos[bl[x]],pos[x]);
      x=fa[bl[x]];
    }
    ans+=ask(1,1,pos[x]);
    return ans;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;++i)cin>>a[i];
    for(int i=1;i<n;++i)
    {
      int x,y;
      scanf("%d%d",&x,&y);
      adde(x,y);adde(y,x);
    }
    dfs1(1);dfs2(1,1);
    build(1,1,n);
    for(int i=1;i<=n;++i)add(1,pos[i],a[i]);
    for(int i=1;i<=n;++i)cout<<pos[i]<<" ";
    cout<<endl;
    while(m--)
    {
      int t,x;ll w;
      cin>>t>>x;
      if(t==1){cin>>w;addall(1,pos[x],pos[x],w);}
      else if(t==2){cin>>w;addall(1,pos[x],mx[x],w);}
      else {cout<<query(x)<<endl;}
    }
    return 0;
}
[HAOI2015]樹上操作(w菌)


[HAOI2015]樹上操作