zoukankan      html  css  js  c++  java
  • LOJ 2553 「CTSC2018」暴力写挂——边分治+虚树

    题目:https://loj.ac/problem/2553

    第一棵树上的贡献就是链并,转化成 ( dep[ x ] + dep[ y ] + dis( x, y ) ) / 2 ,就可以在第一棵树上写边分治,把两边的点到第二棵树上建虚树,在虚树上 DP ,那么虚树上的当前点就是它不同子树里点的 lca ,所以记 dp[ cr ][ 0/1 ] 表示该点子树里 “第一棵树边分治的两个点集” 里最大的两个贡献;用当前点的深度作为 “第二棵树的 lca 深度” 来更新答案即可。

    一直 TLE 。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    int rdn()
    {
      int ret=0;bool fx=1;char ch=getchar();
      while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
      while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
      return fx?ret:-ret;
    }
    ll Mx(ll a,ll b){return a>b?a:b;}
    ll Mn(ll a,ll b){return a<b?a:b;}
    const int N=366670; const ll INF=1e16;
    int n,hd[N],xnt=1,to[N<<1],nxt[N<<1],w[N<<1],tw;//xnt=1
    int siz[N],mn,Rt,p[N],tot; bool vis[N];
    ll dep[N],dis[N],ans;
    namespace Tr{
      const int K=18;
      int hd[N],xnt,to[N<<1],nxt[N<<1],w[N<<1];
      int dfn[N],tim,sta[N],top,pre[N][K+5],bin[K+5];
      ll dep[N],dp[N][2]; int tdp[N];
      bool cmp(int u,int v){return dfn[u]<dfn[v];}
      void add(int x,int y,int z)
      {to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;w[xnt]=z;}
      void ini_dfs(int cr,int fa)
      {
        dfn[cr]=++tim; dp[cr][0]=dp[cr][1]=-INF;
        tdp[cr]=tdp[fa]+1; pre[cr][0]=fa;
        for(int t=1,d=fa;(d=pre[d][t-1]);t++)
          pre[cr][t]=d;
        for(int i=hd[cr],v;i;i=nxt[i])
          if((v=to[i])!=fa)
        { dep[v]=dep[cr]+w[i]; ini_dfs(v,cr);}
      }
      void init()
      {
        for(int i=1,u,v,z;i<n;i++)
          {
        u=rdn();v=rdn();z=rdn();
        add(u,v,z); add(v,u,z);
          }
        bin[0]=1;for(int i=1;i<=K;i++)bin[i]=bin[i-1]<<1;
        ini_dfs(1,0);
      }
      int get_lca(int x,int y)
      {
        if(tdp[x]<tdp[y])swap(x,y); int d=tdp[x]-tdp[y];
        for(int t=0;d;t++)
          if(d&bin[t])x=pre[x][t],d^=bin[t];
        if(x==y)return x;
        for(int t=K;t>=0;t--)
          if(pre[x][t]!=pre[y][t])
        x=pre[x][t], y=pre[y][t];
        return pre[x][0];
      }
      void link(int cr,int v)
      {
        ans=Mx(ans,((dp[cr][0]+dp[v][1]+tw)>>1)-dep[cr]);
        ans=Mx(ans,((dp[cr][1]+dp[v][0]+tw)>>1)-dep[cr]);
        dp[cr][0]=Mx(dp[cr][0],dp[v][0]);
        dp[cr][1]=Mx(dp[cr][1],dp[v][1]);
        dp[v][0]=dp[v][1]=-INF;//
      }
      void solve()
      {
        sort(p+1,p+tot+1,cmp);
        sta[top=1]=1;//1
        for(int i=(p[1]==1)+1;i<=tot;i++)
          {
        int cr=p[i], lca=get_lca(cr,sta[top]);
        while(top&&dfn[lca]<dfn[sta[top]])
          {
            if(dfn[lca]<dfn[sta[top-1]])
              link(sta[top-1],sta[top]), top--;
            else
              link(lca,sta[top]), top--;
          }
        if(sta[top]!=lca)sta[++top]=lca;
        sta[++top]=cr;
          }
        for(int i=top-1;i;i--)link(sta[i],sta[i+1]);
        dp[sta[1]][0]=dp[sta[1]][1]=-INF;
      }
    }
    void add(int x,int y,int z){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;w[xnt]=z;}
    void ini_dfs(int cr,int fa)
    {
      for(int i=hd[cr],v;i;i=nxt[i])
        if((v=to[i])!=fa)
          { dep[v]=dep[cr]+w[i]; ini_dfs(v,cr);}
    }
    void get_rt(int cr,int fa,int s)
    {
      siz[cr]=1;
      for(int i=hd[cr],v;i;i=nxt[i])
        if(!vis[i>>1]&&(v=to[i])!=fa)
          {
        get_rt(v,cr,s); siz[cr]+=siz[v];
        int tmp=Mx(siz[v],s-siz[v]);
        if(tmp<mn) mn=tmp, Rt=i;
          }
    }
    void dfs(int cr,int fa,ll lj,bool fx)
    {
      Tr::dp[cr][fx]=lj+dep[cr]; p[++tot]=cr;
      for(int i=hd[cr],v;i;i=nxt[i])
        if(!vis[i>>1]&&(v=to[i])!=fa)
          dfs(v,cr,lj+w[i],fx);
    }
    void solve(int cr,int s)
    {
      vis[cr>>1]=1; tot=0;
      dfs(to[cr],0,0,0); dfs(to[cr^1],0,0,1);
      tw=w[cr]; Tr::solve();
      int v=to[cr], ts=siz[v];
      if(ts>1){ mn=N;get_rt(v,0,ts);solve(Rt,ts);}
      v=to[cr^1]; ts=s-ts;
      if(ts>1){ mn=N;get_rt(v,0,ts);solve(Rt,ts);}
    }
    int main()
    {
      n=rdn();
      for(int i=1,u,v,z;i<n;i++)
        {
          u=rdn();v=rdn();z=rdn();
          add(u,v,z); add(v,u,z);
        }
      Tr::init(); ini_dfs(1,0);
      mn=N;get_rt(1,0,n);solve(Rt,n);
      for(int i=1;i<=n;i++)
        ans=Mx(ans,dep[i]-Tr::dep[i]);
      printf("%lld
    ",ans);
      return 0;
    }
    View Code

    以为是常数不够优秀,所以学习了一下别人的代码。http://blog-wayne.com/2018/05/15/509/

      仔细一想,建虚树的时候每次 logn 地找 lca ,这样岂不是 nlog2n 的?所以不要每次建虚树都找 lca 。

      一开始把所有 n 个点的相邻点之间的 lca 都找出来。令 a[ i ] 存第一棵树里 dfs 序第 i 大的点的标号, lca[ i ] 存 a[ i ] 和 a[ i-1 ] 的 lca 。

      边分治的过程中带一个 l , r ,表示现在整个点集对应第二棵树的 a[ l ] ~ a[ r ] 。那么用 lca[ ] 就可以建虚树了。

      考虑当前点集分成两个之后,怎么更新 a[ ] 和 lca[ ] ;

      在 a[ ] 中找出所有属于边分治一边的点,假设是 a[ 3 ] , a[ 6 ] , a[ 7 ] ... ,那么 a[ 3 ] 对应的 lca 随便, a[ 6 ] 对应的 lca 应该是 lca[ 4 ] , lca[ 5 ] , lca[ 6 ] 中深度最浅的那个;

      这样就能在边分治的过程中维护好 a[ ] 和 lca[ ] 了。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #define ll long long
    #define pb push_back
    #define pii pair<int,int>
    #define mkp make_pair
    using namespace std;
    int rdn()
    {
      int ret=0;bool fx=1;char ch=getchar();
      while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
      while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
      return fx?ret:-ret;
    }
    ll Mn(ll a,ll b){return a<b?a:b;}
    ll Mx(ll a,ll b){return a>b?a:b;}
    const int N=366670<<1; const ll INF=1e15;
    int n,hd[N],xnt=1,to[N<<1],nxt[N<<1],w[N<<1];
    int mn,siz[N],Rt; ll ans,dp2[N],dp[N][2]; bool lx[N];
    vector<pii> vt[N];
    namespace Tr{
      int hd[N],xnt,to[N<<1],nxt[N<<1],w[N<<1],tim;
      int dep[N],a[N],ta[N],lca[N],tlca[N];
      int sta[N],top; ll dp2[N];
      void add(int x,int y,int z)
      {to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;w[xnt]=z;}
      void ini_dfs(int cr,int fa)
      {
        while(top&&dep[sta[top]]>=dep[cr])top--;
        a[++tim]=cr; lca[tim]=sta[top]; sta[++top]=cr;
        dp[cr][0]=dp[cr][1]=-INF;
        for(int i=hd[cr],v;i;i=nxt[i])
          if((v=to[i])!=fa)
        {
          dep[v]=dep[cr]+1; dp2[v]=dp2[cr]+w[i];
          ini_dfs(v,cr);
        }
      }
      void init()
      {
        for(int i=1,u,v,z;i<n;i++)
          u=rdn(),v=rdn(),z=rdn(),add(u,v,z),add(v,u,z);
        ini_dfs(1,0);
      }
      void link(int cr,int v,int tw)
      {
        ans=Mx(ans,((dp[cr][0]+dp[v][1]+tw)>>1)-dp2[cr]);
        ans=Mx(ans,((dp[cr][1]+dp[v][0]+tw)>>1)-dp2[cr]);
        dp[cr][0]=Mx(dp[cr][0],dp[v][0]);
        dp[cr][1]=Mx(dp[cr][1],dp[v][1]);
        dp[v][0]=dp[v][1]=-INF;
      }
      int solve(int l,int r,int tw)
      {
        sta[top=1]=a[l];
        for(int i=l+1;i<=r;i++)
          {
        int lm=dep[lca[i]];
        while(top&&dep[sta[top]]>lm)
          if(dep[sta[top-1]]>lm)
            link(sta[top-1],sta[top],tw), top--;
          else
            link(lca[i],sta[top],tw), top--;
        if(sta[top]!=lca[i])sta[++top]=lca[i];
        sta[++top]=a[i];
          }
        for(int i=top-1;i;i--)link(sta[i],sta[i+1],tw);
        dp[sta[1]][0]=dp[sta[1]][1]=-INF;
        int mid=l-1;
        for(int i=l,tl=0;i<=r;i++)
          {
        if(!tl||dep[lca[i]]<dep[tl])tl=lca[i];
        if(!lx[a[i]])
          { ta[++mid]=a[i]; tlca[mid]=tl; tl=0;}
          }
        int ret=mid;
        for(int i=l,tl=0;i<=r;i++)
          {
        if(!tl||dep[lca[i]]<dep[tl])tl=lca[i];
        if(lx[a[i]])
          { ta[++mid]=a[i]; tlca[mid]=tl; tl=0;}
          }
        for(int i=l;i<=r;i++)a[i]=ta[i];
        for(int i=l;i<=r;i++)lca[i]=tlca[i];
        return ret;
      }
    }
    void add(int x,int y,int z)
    {
      to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;w[xnt]=z;
      to[++xnt]=x;nxt[xnt]=hd[y];hd[y]=xnt;w[xnt]=z;
    }
    void del_ed(int x,int y)
    {
      if(to[hd[x]]==y)hd[x]=nxt[hd[x]];
      else
        {
          for(int i=hd[x],pr;i;pr=i,i=nxt[i])
        if(to[i]==y)nxt[pr]=nxt[i];
        }
      if(to[hd[y]]==x)hd[y]=nxt[hd[y]];
      else
        {
          for(int i=hd[y],pr;i;pr=i,i=nxt[i])
        if(to[i]==x)nxt[pr]=nxt[i];
        }
    }
    void Rbuild(int cr,int fa)
    {
      for(int i=0,lst=0,lm=vt[cr].size();i<lm;i++)
        {
          int v=vt[cr][i].first, z=vt[cr][i].second;
          if(v==fa)continue;
          if(!lst)add(cr,v,z),lst=cr;
          else{ n++; add(lst,n,0); add(n,v,z); lst=n;}
        }
      for(int i=0,v,lm=vt[cr].size();i<lm;i++)
        if((v=vt[cr][i].first)!=fa) Rbuild(v,cr);
    }
    void ini_dfs(int cr,int fa)
    {
      for(int i=hd[cr],v;i;i=nxt[i])
        if((v=to[i])!=fa)
          { dp2[v]=dp2[cr]+w[i]; ini_dfs(v,cr);}
    }
    void get_rt(int cr,int fa,int s)
    {
      siz[cr]=1;
      for(int i=hd[cr],v;i;i=nxt[i])
        if((v=to[i])!=fa)
          {
        get_rt(v=to[i],cr,s); siz[cr]+=siz[v];
        int mx=Mx(siz[v],s-siz[v]);
        if(mx<mn)mn=mx,Rt=i;
          }
    }
    void dfs(int cr,int fa,ll lj,bool fx)
    {
      dp[cr][fx]=lj+dp2[cr]; dp[cr][!fx]=-INF; lx[cr]=fx;
      for(int i=hd[cr],v;i;i=nxt[i])
        if((v=to[i])!=fa) dfs(v,cr,lj+w[i],fx);
    }
    void solve(int cr,int s,int l,int r)
    {
      int u=to[cr^1], v=to[cr]; del_ed(u,v);
      dfs(u,0,0,0); dfs(v,0,0,1);
      int mid=Tr::solve(l,r,w[cr]);
      int ts=siz[v];
      if(ts>1){mn=N;get_rt(v,0,ts);solve(Rt,ts,mid+1,r);}
      ts=s-ts;
      if(ts>1){mn=N;get_rt(u,0,ts);solve(Rt,ts,l,mid);}
    }
    int main()
    {
      n=rdn();
      for(int i=1,u,v,z;i<n;i++)
        {
          u=rdn();v=rdn();z=rdn();
          vt[u].pb(mkp(v,z)); vt[v].pb(mkp(u,z));
        }
      Tr::init(); int yn=n;
      Rbuild(1,0); ini_dfs(1,0);
      mn=N;get_rt(1,0,n);solve(Rt,n,1,yn);
      for(int i=1;i<=n;i++)
        ans=Mx(ans,dp2[i]-Tr::dp2[i]);
      printf("%lld
    ",ans); return 0;
    }
    View Code

      后来发现之前 TLE 是忘了写 rebuild ... 不过懒得改了。

  • 相关阅读:
    数学之美 系列十一 Google 阿卡 47 的制造者阿米特.辛格博士
    IE6 中的最大最小寬度和高度 css 高度 控制(兼容版本)
    数学之美 系列一 统计语言模型
    数学之美 系列八 贾里尼克的故事和现代语言处理
    数学之美 系列二 谈谈中文分词
    数学之美系列 4 怎样度量信息?
    带线的无限级下拉树列表完整示例篇
    实战篇通用的页面列表导出Excel控件
    CYQ.Data 轻量数据层之路 V3.0版本发布Xml绝对杀手(三十二)
    秋色园Blog 博客系列索引
  • 原文地址:https://www.cnblogs.com/Narh/p/10483598.html
Copyright © 2011-2022 走看看