Live2D Demo

Codeforces Gym 102538 G. Giant Penguin (300iq Contest 3) 题解

题目链接

不错的点分树题。我之前只听说过有点分树这个东西,没做过几个题。

先考虑图是一棵树的情况怎么做。我们对这棵树建立点分树。所谓点分树,就是先对树进行重心分治(点分治),然后把相邻两层的重心连边形成一棵新的树。容易发现新的树的高度是\(O(logn)\)的。看一开始重心分治的过程,在重心分治的每一层里,我们处理原树中所有满足"两个端点都在这一层内,且端点分别在重心的不同子树"的路径,路径指的是从标记点到询问点的路径。比如当前层的重心是c,我们要处理其两个不同子树内点x和点y之间的路径。在点分树中,c肯定是x和y的LCA。假设我们在某一次修改时标记了x,之后又询问了y,为了让x能更新到y的答案,我们给每个点设置一个权值\(val_i\),在标记x时给它的所有(点分树上的)祖先的\(val_i\)都更新上从那个祖先到x的距离;询问y时枚举y的所有祖先,用每个祖先的\(val_i\)加上从它到y的距离更新答案即可。这样每对(x,y)都在其LCA处(也就是它们被分开的那一层的重心)被统计了。时间复杂度\(O(nlogn)\)

原问题可以用类似的方法做。我们取原图的一棵生成树建点分树。先来分析重心分治的过程。在分治的每一层里,我们要统计的是在完全这一层内部(不经过这一层外面的点)且跨越子树的路径,有以下两种:


由于每个节点都在最多10个不经过重复点的环内,所以在层内所有的边中,最多只有k条是跨越子树的,因为每条这种边都能构成至少一个包含重心的简单环。层内边指的是两个端点都在层内的边。发现这一层内需要统计的所有路径,要么经过重心,要么经过至少一条跨越子树的边。把重心设为"关键点",每条跨越子树的边也随便选一个点作为关键点,则每条要统计的边至少经过一个关键点,且每层关键点个数不超过k+1个。不妨像树的情况在重心处统计路径那样,我们在这些关键点处统计路径。我们以每个关键点为原点,用层内的点和边跑bfs,求出每个关键点到层内每个点的最短路。类似地,在标记一个点x时,枚举它(点分树上)所有的祖先,并更新这个祖先所在层的所有关键点的值。询问时类似。注意不要用map存距离,多一个log卡不过去的。(亲身试验) 可以用\(dist[pos][lev][i]\)表示点pos到它的第lev级祖先所在层的第i个关键点的距离。

时间复杂度\(O(nklogn)\)

点击查看代码
#include <bits/stdc++.h>

#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back

void fileio()
{
  #ifdef LGS
  freopen("in.txt","r",stdin);
  freopen("out.txt","w",stdout);
  #endif
}
void termin()
{
  #ifdef LGS
  std::cout<<"\n\nPROGRAM TERMINATED";
  #endif
  exit(0);
}

using namespace std;

int n,m,t,fa[100010],sz[100010],col[100010],len,dis[100010],finalpar[100010],dep[100010],dist[100010][20][12],inList[100010],mark=0;
vector <int> g[100010],tg[100010],ttg[100010],List;
vector <pii> e,implist[100010];
vector <pair <pii,pii> > dists;//{{id,up},{dn,dist}}
bool ban[100010];
pii mn;

int Find(int x){return fa[x]==x ? x:fa[x]=Find(fa[x]);}

void dfsSz(int pos,int par)
{
  sz[pos]=1;
  rep(i,tg[pos].size()) if(tg[pos][i]!=par&& !ban[tg[pos][i]]) dfsSz(tg[pos][i],pos),sz[pos]+=sz[tg[pos][i]];
}
void getCen(int pos,int par,int tot)
{
  int val=tot-sz[pos];
  rep(i,tg[pos].size()) if(tg[pos][i]!=par&& !ban[tg[pos][i]]) val=max(val,sz[tg[pos][i]]),getCen(tg[pos][i],pos,tot);
  mn=min(mn,mpr(val,pos));
}
void dfsColor(int pos,int par)
{
  col[pos]=len;List.pb(pos);
  rep(i,tg[pos].size()) if(tg[pos][i]!=par&& !ban[tg[pos][i]]) dfsColor(tg[pos][i],pos);
}
int solve(int pos,int d)//建立点分树
{
  dfsSz(pos,0);
  mn=mpr(1e9,1e9);getCen(pos,0,sz[pos]);
  pos=mn.se;dep[pos]=d;
  len=0;List={pos};
  rep(i,tg[pos].size()) if(!ban[tg[pos][i]])
  {
    ++len;
    dfsColor(tg[pos][i],pos);
  }
  vector <int> imp={pos};
  ++mark;rep(i,List.size()) inList[List[i]]=mark;
  rep(i,List.size()) rep(j,g[List[i]].size())
    if(!ban[g[List[i]][j]]&&col[List[i]]!=col[g[List[i]][j]]&&List[i]!=pos&&g[List[i]][j]!=pos&&inList[g[List[i]][j]]==mark)
      imp.pb(min(List[i],g[List[i]][j]));
  sort(imp.begin(),imp.end());imp.erase(unique(imp.begin(),imp.end()),imp.end());
  rep(i,imp.size()) implist[pos].pb(mpr(imp[i],1e9));
  rep(i,imp.size())
  {
    rep(j,List.size()) dis[List[j]]=1e9;
    queue <LL> qq;qq.push(imp[i]);dis[imp[i]]=0;
    while(!qq.empty())
    {
      int f=qq.front();qq.pop();
      rep(j,g[f].size()) if(!ban[g[f][j]]&&dis[g[f][j]]>dis[f]+1)
      {
        dis[g[f][j]]=dis[f]+1;
        qq.push(g[f][j]);
      }
    }
    rep(j,List.size()) dists.pb(mpr(mpr(i,pos),mpr(List[j],dis[List[j]])));
  }
  ban[pos]=true;
  rep(i,tg[pos].size()) if(!ban[tg[pos][i]])
  {
    int nxt=solve(tg[pos][i],d+1);
    ttg[pos].pb(nxt);finalpar[nxt]=pos;
  }
  return pos;
}

void chmin(int &x,int y){if(x>y) x=y;}

int main()
{
  fileio();

  cin>>n>>m>>t;
  int x,y;
  rep(i,m)
  {
    scanf("%d%d",&x,&y);
    g[x].pb(y);g[y].pb(x);
    e.pb(mpr(x,y));
  }
  repn(i,n) fa[i]=i;
  rep(i,e.size()) if(Find(e[i].fi)!=Find(e[i].se))
  {
    tg[e[i].fi].pb(e[i].se);tg[e[i].se].pb(e[i].fi);
    fa[Find(e[i].fi)]=Find(e[i].se);
  }
  int root=solve(1,0);
  rep(i,dists.size()) dist[dists[i].se.fi][dep[dists[i].se.fi]-dep[dists[i].fi.se]][dists[i].fi.fi]=dists[i].se.se;

  int q;cin>>q;
  rep(qn,q)
  {
    scanf("%d%d",&x,&y);
    if(x==1)
    {
      int cur=y,cc=0;
      while(true)
      {
        rep(i,implist[cur].size()) chmin(implist[cur][i].se,dist[y][cc][i]);
        if(cur==root) break;
        cur=finalpar[cur];++cc;
      }
    }
    else
    {
      int ans=1e9,cur=y,cc=0;
      while(true)
      {
        rep(i,implist[cur].size()) chmin(ans,implist[cur][i].se+dist[y][cc][i]);
        if(cur==root) break;
        cur=finalpar[cur];++cc;
      }
      printf("%d\n",ans);
    }
  }

  termin();
}

PS: 官方题解英语水平太差了,完全看不懂

posted @ 2022-11-03 21:40  LegendStane  阅读(234)  评论(0)    收藏  举报