zoukankan      html  css  js  c++  java
  • [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?!!

  • 相关阅读:
    生日蜡烛——第七届蓝桥杯C语言B组(省赛)第二题
    积分之谜——第六届蓝桥杯C语言B组(国赛)第一题
    平方十位数——第八届蓝桥杯JavaB组(国赛)第一题
    激光样式——第九届蓝桥杯C语言B组(国赛)第二题
    换零钞——第九届蓝桥杯C语言B组(国赛)第一题
    煤球数目——第七届蓝桥杯C语言B组(省赛)第一题
    扫描算法(SCAN)——磁盘调度管理
    最短寻道优先算法(SSTF)——磁盘调度管理
    最近最少使用算法(LRU)——页面置换
    ssm返回jsonp数据格式
  • 原文地址:https://www.cnblogs.com/ck666/p/7488005.html
Copyright © 2011-2022 走看看