[ZJOI2008]树的统计Count

[ZJOI2008]树的统计Count

2017-09-07


Description

  一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成
一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I
II. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身


Input

  输入的第一行为一个整数n,表示节点的个数。接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有
一条边相连。接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。接下来1行,为一个整数q,表示操作
的总数。接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。 
对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。


Output

  对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。


Sample Input

4
1 2
2 3
4 1
4 2 1 3
12
QMAX 3 4
QMAX 3 3
QMAX 3 2
QMAX 2 3
QSUM 3 4
QSUM 2 1
CHANGE 1 5
QMAX 3 4
CHANGE 3 6
QMAX 3 4
QMAX 2 4
QSUM 3 4

Sample Output

4
1
2
2
10
6
5
6
5
16

这个样例我也是醉了,dfs序和树的点数是一样的,导致dfs出错时完全看不出来.....
现在说树链剖分
所谓树链剖分,就是把一棵树压缩进一颗线段树,把树的每一个点在线段树里分配一个新的标签。
在第一遍dfs的时候找到每一个节点(包括他本身)有多少节点,然后根据节点数分配时重链还是轻链。。
对于每一个点都是先找重链,再轻链...分配id。。扫完根节点之后根据id加入线段树...注意dfs序满足每一颗子树的dfs再统一区间..
现在我们要修改一棵树上的单点,通过pos[i]修改树的时候找到这个标签所对应的地方...区间修改就是先找到链头,然后再线段树分区间加(-|*)
①每一次当前点到链头->跳到另一条链->是否在一条链?
否执行①,是在一条链上,->区间从小到大查询加入答案.>完成查询.;修改同理..
我是先建树,V是按照新id放进线段树里的新权值
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
const int maxn=50000+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];
ll n,m,size[maxn],V[maxn];
struct saber{
ll sum,ma,l,r;
}tr[maxn*4];
bool vis[maxn];
struct sab{
int nex,to;
}b[maxn<<1];
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 VV(){for(int i=1;i<=n;i++)V[pos[i]]=v[i];}
void build(int k,int l,int r){
    tr[k].l=l;tr[k].r=r;
    if(l==r) {tr[k].ma=V[l];tr[k].sum=V[l];return;}
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;
    tr[k].ma=max(tr[k<<1].ma,tr[k<<1|1].ma);
}
ll askmax(int k,int i,int j){
    int l=tr[k].l,r=tr[k].r,mid=(l+r)>>1;
    if(l==i&&j==r){return tr[k].ma;}
    if(j<=mid)return askmax(k<<1,i,j);
    else if(i>mid)return askmax(k<<1|1,i,j);
    else return max(askmax(k<<1,i,mid),askmax(k<<1|1,mid+1,j));
}
ll ask(int k,int i,int j){
    int l=tr[k].l,r=tr[k].r,mid=(l+r)>>1;
    if(l==i&&j==r){return tr[k].sum;}
    if(j<=mid)return ask(k<<1,i,j);
    else if(i>mid)return ask(k<<1|1,i,j);
    else return ask(k<<1,i,mid)+ask(k<<1|1,mid+1,j);
}
void change(int k,int dang,int da){
    int l=tr[k].l,r=tr[k].r,mid=(l+r)>>1;
    if(l==r&&l==dang){tr[k].sum=da;tr[k].ma=da;return ;}
    if(dang<=mid)change(k<<1,dang,da);
    else change(k<<1|1,dang,da);
    tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;
    tr[k].ma=max(tr[k<<1].ma,tr[k<<1|1].ma);
}
ll ask(int x,int y){
    ll ans=0;
    while(belong[x]!=belong[y]){
        if(deep[belong[x]]<deep[belong[y]])swap(x,y);
        ans+=ask(1,pos[belong[x]],pos[x]);
        x=fa[belong[x]];
    }
    if(pos[x]>pos[y])swap(x,y);
    ans+=ask(1,pos[x],pos[y]);
    return ans;
}
ll askmax(int x,int y){
    ll ans=-9999999;
    while(belong[x]!=belong[y]){
        if(deep[belong[x]] < deep[belong[y]])swap(x,y);
        ans=max(ans,askmax(1,pos[belong[x]],pos[x]));
        x=fa[belong[x]];
    }
    if(pos[x]>pos[y])swap(x,y);
    ans=max(ans,askmax(1,pos[x],pos[y]));
    return ans;
}
int main(){
    n=read();
    for(int i=1;i<n;i++){
        int x,y;
        x=read();y=read();
        add(x,y);add(y,x);
    }
    for(int i=1;i<=n;i++)v[i]=read();
    dfs(1);
    dfs2(1,1);
    VV();
    build(1,1,n);
    m=read();
    while(m){m--;
        int y,z;char kk[10];
        cin>>kk;y=read();z=read();
        if(kk[0]=='C'){v[y]=z;
        change(1,pos[y],z);}//把u改成t
        else if(kk[1]=='M'){
        cout<<askmax(y,z)<<endl;}//询问max 
        else {
        cout<<ask(y,z)<<endl;}//询问和 
    }
    return 0;
}
[ZJOI2008]树的统计

by:s_a_b_e_r


这个样例我也是醉了+1
树剖裸题……学习一下树剖w
所谓树剖就是把本来好好的一棵树剖成好几条链,然后一字排开的丢进线段树里,就可以实现每一条链区间O(logn)快速查询。
然后凭借特殊的剖链技巧,可以实现每个点到根节点最多有logn条链,总计查询复杂度O(log2n)
树剖先进行两遍dfs,第一遍求出各点的深度、父节点以及子树(包括这个点)的大小(size)
第二遍开始剖链,每次选一个size最大的儿子继承他的财产重链,剩下的儿子白手起家重新开一条轻链
然后每次查询的时候把两个点靠下的那个向上跳一条链,直到两个点在同一条链上,完成查询
线段树风格不一样导致沟通困难……考虑学一下s酱的线段树?
#include<iostream>
#include<cstdio>
#define LL long long
using namespace std;
const int N=30009,inf=1000009;
int n,m,cnt,p[N],q,inx;
int size[N],dep[N],fa[N],bl[N],pos[N],mx[N];
char s[10];
LL a[N];
struct edge{
int to,nex;
}e[N<<1];
struct node{
int l,r;
LL sum,ma;
}t[N<<2];
void adde(int u,int v)
{
     ++cnt;
     e[cnt].to=v;
     e[cnt].nex=p[u];
     p[u]=cnt;
}
void update(int k)
{
     t[k].sum=t[k<<1].sum+t[k<<1|1].sum;
     t[k].ma=max(t[k<<1].ma,t[k<<1|1].ma);
}
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)
{
     t[k].l=l,t[k].r=r;
     if(l==r){return;}
     int mid=(l+r)>>1;
     build(k<<1,l,mid);
     build(k<<1|1,mid+1,r);
     update(k);
}
void change(int k,int x,int w)
{
     int l=t[k].l,r=t[k].r;
     if(l==r){t[k].sum=t[k].ma=w;return;}
     int mid=(l+r)>>1;
     if(x<=mid)change(k<<1,x,w);
     else change(k<<1|1,x,w);
     update(k);
}
LL asksum(int k,int L,int R)
{
    int l=t[k].l,r=t[k].r;
    if(r<L||l>R)return 0;
    if(L<=l&&R>=r)return t[k].sum;
    return asksum(k<<1,L,R)+asksum(k<<1|1,L,R);
}
LL qsum(int u,int v)
{
    LL ans=0;
    while(bl[u]!=bl[v])
    {
      if(dep[bl[u]]<dep[bl[v]])swap(u,v);
      ans+=asksum(1,pos[bl[u]],pos[u]);
      u=fa[bl[u]];
    }
    if(pos[u]>pos[v])swap(u,v);
    ans+=asksum(1,pos[u],pos[v]);
    return ans;
}
LL askma(int k,int L,int R)
{
    int l=t[k].l,r=t[k].r;
    if(r<L||l>R)return -inf;
    if(L<=l&&R>=r)return t[k].ma;
    return max(askma(k<<1,L,R),askma(k<<1|1,L,R));
}
LL qma(int u,int v)
{
    LL ans=-inf;
    while(bl[u]!=bl[v])
    {
      if(dep[bl[u]]<dep[bl[v]])swap(u,v);
      ans=max(ans,askma(1,pos[bl[u]],pos[u]));
      u=fa[bl[u]];
    }
    if(pos[u]>pos[v])swap(u,v);
    ans=max(ans,askma(1,pos[u],pos[v]));
    return ans;
}
int main()
{
    cin>>n;
    for(int i=1;i<n;++i)
    {
      int x,y;
      scanf("%d%d",&x,&y);
      adde(x,y);adde(y,x);
    }
    for(int i=1;i<=n;++i)cin>>a[i];
    dfs1(1);dfs2(1,1);
    build(1,1,n);
    for(int i=1;i<=n;++i)change(1,pos[i],a[i]);
    scanf("%d",&q);
    while(q--)
    {
      int a,b;
      cin>>s;
      scanf("%d%d",&a,&b);
      if(s[0]=='C')   //change
      change(1,pos[a],b);
      else if(s[1]=='M')//qmax
      printf("%lld
",qma(a,b));
      else            //qsum
      printf("%lld
",qsum(a,b));
    }
    return 0;
}
bzoj 1036 Count

 s:下面是叫bz和luogu的区别,左边luogu,右边bz


w: luogu:你交程序了啊,来我帮你看看,好的你可以AC了

    bz:【打出一排RE】你***还想AC?!!

原文地址:https://www.cnblogs.com/ck666/p/7488005.html