zoukankan      html  css  js  c++  java
  • bzoj 3779 重组病毒 —— LCT+树状数组(区间修改+区间查询)

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3779

    RELEASE操作可以对应LCT的 access,RECENTER则是 makeroot;

    考虑颜色数,把一条实边变成虚边,子树+1,虚变实子树-1;

    但有换根操作,怎么维护子树?

    也可以用 dfs 序线段树维护,其实换 rt 只是 splay 的根方向改变,对应的子树还是可以找到的;

    注意虚边变实或实边变虚时要找子树,不是直接找那个儿子,而是找那个儿子所在 splay 的根;

    然后这里 splay 的 reverse 就要打上标记的同时已经交换子树,否则找儿子时可能会错(?);

    一开始写了树剖+线段树的复杂版本,总是不对...

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define mid ((l+r)>>1)
    #define ls (x<<1)
    #define rs (x<<1|1)
    using namespace std;
    int const xn=1e5+5;
    int n,rt,pre[xn],c[xn][2],sta[xn],tp,rev[xn],hd[xn],ct,to[xn<<1],nxt[xn<<1];
    int tim,fa[xn],dfn[xn],sum[xn<<1],lzy[xn<<1],top[xn],siz[xn],son[xn],dep[xn];
    char dc[20];
    int rd()
    {
      int ret=0,f=1; char ch=getchar();
      while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();}
      while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
      return f?ret:-ret;
    }
    void add(int x,int y){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct;}
    void turn(int x,int s,int len){sum[x]+=s*len; lzy[x]+=s;}//*len
    void pushdown(int x,int l,int r)
    {
      if(!lzy[x])return;
      turn(ls,lzy[x],mid-l+1); turn(rs,lzy[x],r-mid);
      lzy[x]=0;
    }
    void pushup(int x){sum[x]=sum[ls]+sum[rs];}
    void update(int x,int l,int r,int L,int R,int s)
    {
      if(L>R)return;
      if(l>=L&&r<=R){turn(x,s,r-l+1); return;}
      pushdown(x,l,r);
      if(mid>=L)update(ls,l,mid,L,R,s);
      if(mid<R)update(rs,mid+1,r,L,R,s);
      pushup(x);
    }
    int query(int x,int l,int r,int L,int R)
    {
      if(L>R)return 0;
      if(l>=L&&r<=R)return sum[x];
      pushdown(x,l,r); int ret=0;
      if(mid>=L)ret+=query(ls,l,mid,L,R);
      if(mid<R)ret+=query(rs,mid+1,r,L,R);
      return ret;
    }
    void dfs(int x,int ff)
    {
      fa[x]=pre[x]=ff;//self splay
      dep[x]=dep[ff]+1; siz[x]=1;
      for(int i=hd[x],u;i;i=nxt[i])
        if((u=to[i])!=ff)
          {
        dfs(u,x); siz[x]+=siz[u];
        if(siz[u]>siz[son[x]])son[x]=u;
          }
    }
    void dfs2(int x)
    {
      dfn[x]=++tim;
      update(1,1,n,dfn[x],dfn[x],dep[x]);
      if(son[x])top[son[x]]=top[x],dfs2(son[x]);
      for(int i=hd[x],u;i;i=nxt[i])
        if((u=to[i])!=son[x]&&u!=fa[x])top[u]=u,dfs2(u);
    }
    void torev(int x){rev[x]^=1; swap(c[x][0],c[x][1]);}//
    void reverse(int x)
    {
      if(!rev[x])return;
      //swap(c[x][0],c[x][1]);
      //rev[c[x][0]]^=1; rev[c[x][1]]^=1;
      if(c[x][0])torev(c[x][0]);
      if(c[x][1])torev(c[x][1]);
      rev[x]=0;
    }
    bool isroot(int x){return c[pre[x]][0]!=x&&c[pre[x]][1]!=x;}
    void rotate(int x)
    {
      int y=pre[x],z=pre[y],d=(c[y][1]==x);
      if(!isroot(y))c[z][c[z][1]==y]=x;
      pre[x]=z; pre[y]=x; pre[c[x][!d]]=y;
      c[y][d]=c[x][!d]; c[x][!d]=y;
    }
    void splay(int x)
    {
      //reverse!
      sta[tp=1]=x;
      for(int i=x;!isroot(i);i=pre[i])sta[++tp]=pre[i];
      while(tp)reverse(sta[tp--]);
      while(!isroot(x))
        {
          int y=pre[x],z=pre[y];
          if(!isroot(y))
        {
          if((c[y][0]==x)^(c[z][0]==y))rotate(x);
          else rotate(y);
        }
          rotate(x);
        }
    }
    int find(int x,int y)//y in xtree
    {
      //while(top[y]!=top[x])y=fa[top[y]];//?
      while(dep[x]<dep[top[y]])
        {
          y=top[y];
          if(fa[y]==x)return y;
          y=fa[y];
        }
      //while(fa[y]!=x)y=fa[y];
      //return y;
      return son[x];//
    }
    void change(int x,int v)
    {
      if(dfn[rt]>dfn[x]&&dfn[rt]<=dfn[x]+siz[x]-1)
        {
          int y=find(x,rt);
          update(1,1,n,1,dfn[y]-1,v);
          update(1,1,n,dfn[y]+siz[y],n,v);
        }
      else update(1,1,n,dfn[x],dfn[x]+siz[x]-1,v);
    }
    int findrt(int x)
    {
      while(c[x][0])x=c[x][0];
      return x;
    }
    void access(int x)
    {
      bool fl=0;
      for(int t=0;x;x=pre[x])
        {
          splay(x);
          if(c[x][1])change(findrt(c[x][1]),1);//
          c[x][1]=t;
          if(t)change(findrt(t),-1);//
          t=x;
        }
    }
    void makeroot(int x)
    {
      access(x); splay(x); torev(x);//
    }
    int main()
    {
      n=rd(); int m=rd();
      for(int i=1,x,y;i<n;i++)x=rd(),y=rd(),add(x,y),add(y,x);
      rt=1; dfs(1,0); top[1]=1; dfs2(1);
      for(int i=1,x;i<=m;i++)
        {
          scanf("%s",dc); scanf("%d",&x);
          if(dc[2]=='L')access(x);
          if(dc[2]=='C')makeroot(x),rt=x;
          if(dc[2]=='Q')
        {
          int ret=0,sz;
          if(dfn[rt]>dfn[x]&&dfn[rt]<=dfn[x]+siz[x]-1)//in subtree
            {
              int y=find(x,rt); sz=n-siz[y];
              ret+=query(1,1,n,1,dfn[y]-1);
              ret+=query(1,1,n,dfn[y]+siz[y],n);
            }
          else if(rt==x)ret=query(1,1,n,1,n),sz=n;//!!!
          else ret=query(1,1,n,dfn[x],dfn[x]+siz[x]-1),sz=siz[x];
          printf("ret=%d sz=%d
    ",ret,sz);
          printf("%.7f
    ",1.0*ret/sz);
        }
        }
      return 0;
    }

    然后听说线段树会被卡,要写树状数组,于是干脆把那个未知错误的代码扔了;

    但是...树状数组维护序列可以单点修改区间查询,维护差分序列可以区间修改单点查询,如何区间修改区间查询?

    找题解,学到新知识了——区间修改区间查询的树状数组!

    树状数组里放两个数组,一个维护差分序列 ( d[i] ),另一个维护 ( d[i]*i );

    为什么这样?因为首先,要区间修改,只能维护差分数组;

    考虑如何从差分数组得到原数组的前缀和 ( s[p] ),就是 ( sumlimits_{i=1}^{p} sumlimits_{j=1}^{i} d[j] )

    发现一个 ( d[j] ) 被算了 ( p-j+1 ) 次,也就是 ( (p+1)-j ) 次;

    所以只要维护 ( d[i] ) 和 ( d[i]*i ),到时候查询 ( p ) 位置的原序列前缀和,就是 ( (p+1)*sumlimits_{i=1}^{p} d[i] - sumlimits_{i=1}^{p}d[i]*i );

    所以这个似乎很好用的样子;

    一晚上栽在 splay 之前的一系列 reverse 上,又分不清 i 和 x 了呵呵,然而实际上这题做了一天。

    代码如下:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    int const xn=1e5+5;
    int n,m,rt,hd[xn],ct,to[xn<<1],nxt[xn<<1],sta[xn],top,rev[xn],dep[xn];
    int tim,fa[xn][20],pre[xn],c[xn][2],dfn[xn],ed[xn];
    ll s[xn],g[xn];
    int rd()
    {
      int ret=0,f=1; char ch=getchar();
      while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();}
      while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
      return f?ret:-ret;
    }
    void add(int x,int y){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct;}
    void update(int x,ll v){for(int i=x;i<=n;i+=(i&-i))s[i]+=v,g[i]+=x*v;}//g[i]+=x*v
    ll query(int x)
    {
      if(x>n)return 0;
      ll s1=0,s2=0;
      for(int i=x;i;i-=(i&-i))s1+=s[i],s2+=g[i];
      return (x+1)*s1-s2;
      }
    void dfs(int x,int ff)
    {
      pre[x]=fa[x][0]=ff;
      for(int i=1;fa[fa[x][i-1]][i-1];i++)fa[x][i]=fa[fa[x][i-1]][i-1];
      dfn[x]=++tim; dep[x]=dep[ff]+1;
      for(int i=hd[x],u;i;i=nxt[i])
        if((u=to[i])!=ff)dfs(u,x);
      ed[x]=tim;
      update(dfn[x],1); update(ed[x]+1,-1);
    }
    bool isroot(int x){return c[pre[x]][0]!=x&&c[pre[x]][1]!=x;}
    void rever(int x)
    {
      rev[x]^=1;
      swap(c[x][0],c[x][1]);
    }
    void pushdn(int x)
    {
      if(!rev[x])return;
      rever(c[x][0]); rever(c[x][1]);
      rev[x]=0;
    }
    void rotate(int x)
    {
      int y=pre[x],z=pre[y],d=(c[y][1]==x);
      if(!isroot(y))c[z][c[z][1]==y]=x;
      pre[x]=z; pre[y]=x; pre[c[x][!d]]=y;
      c[y][d]=c[x][!d]; c[x][!d]=y;
    }
    void splay(int x)
    {
      sta[top=1]=x;
      for(int i=x;!isroot(i);i=pre[i])sta[++top]=pre[i];//i!!!
      while(top)pushdn(sta[top--]);
      while(!isroot(x))
        {
          int y=pre[x],z=pre[y];
          if(!isroot(y))
        ((c[y][0]==x)^(c[z][0]==y))?rotate(x):rotate(y);
          rotate(x);
        }
        }
    int find(int x,int y)//y in x's subtree
    {
      for(int i=19;i>=0;i--)
        if(dep[fa[y][i]]>dep[x])y=fa[y][i];
      return y;
    }
    void change(int x,int v)
    {
      while(c[x][0])pushdn(x),x=c[x][0];
      if(rt==x)update(1,v);
      else if(dfn[rt]>dfn[x]&&dfn[rt]<=ed[x])
        {
          int y=find(x,rt);
          update(1,v); update(dfn[y],-v); update(ed[y]+1,v);
        }
      else update(dfn[x],v),update(ed[x]+1,-v);
    }
    void access(int x)
    {
      for(int t=0;x;x=pre[x])
        {
          splay(x);
          if(c[x][1])change(c[x][1],1);
          c[x][1]=t;
          if(t)change(t,-1);
          t=x;
        }
    }
    void makeroot(int x)
    {
      access(x); splay(x); rever(x);
    }
    double getans(int x)
    {
      if(rt==x)return 1.0*query(n)/n;
      if(dfn[rt]>dfn[x]&&dfn[rt]<=ed[x])
        {
          int y=find(x,rt);
          ll ret=query(n)-query(ed[y])+query(dfn[y]-1);
          return 1.0*ret/(n-(ed[y]-dfn[y]+1));
        }
      ll ret=query(ed[x])-query(dfn[x]-1);
      return 1.0*ret/(ed[x]-dfn[x]+1);
    }
    char dc[20];
    int main()
    {
      n=rd(); m=rd();
      for(int i=1,x,y;i<n;i++)x=rd(),y=rd(),add(x,y),add(y,x);
      dfs(1,0);
      for(int i=1,x;i<=m;i++)
        {
          scanf("%s",dc); x=rd();
          if(dc[2]=='L')access(x);
          if(dc[2]=='C')makeroot(x),rt=x;
          if(dc[2]=='Q')printf("%.10f
    ",getans(x));
        }
      return 0;
    }
  • 相关阅读:
    JAX-RS:@PathVariable @RequestParam @ModelAttribute等参数绑定注解详解
    关于重定向RedirectAttributes的用法
    数据库事务的四大特性以及事务的隔离级别
    电脑打开任务管理器出现卡顿
    IDEA: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value
    git学习命令
    python 输入 与如何查看文档 小结
    python formatters 与字符串 小结 (python 2)
    Hibernate 配置文件与实体类
    python编码问题 与 代码换行问题
  • 原文地址:https://www.cnblogs.com/Zinn/p/10124183.html
Copyright © 2011-2022 走看看