CF983E NN country

NN country

题目描述
传送门:http://codeforces.com/contest/983/problem/E

题解
首先有一个很显然的贪心策略,我们对于每个节点预处理出从它出发向上乘一次车最远能到哪。
对于一次询问,两个点x,y,我们先让这两个点贪心地往lca方向跑。
这样x点跑了a次到了lx点,y点跑了b次到了rx点。
如果lx到rx可以乘一次车到达,那么答案是a+b+1,否则答案就是a+b+2。
所以问题就转化成了给两个点x,y,询问它们的子树中是否存在可以直达的路径。
一种暴力办法是直接上树套树,树状数组套个set就好了,题目时限3秒,在cf上应该能过。
考虑少一个log的做法。
我们对于一组点lx,rx,它们对应的dfs序区间是 [L[lx],R[lx]] 和 [L[rx],R[rx]]。
先dfs一遍这棵树,对于每条巴士线路,当访问到它的起点的时候,将它的终点赋上权值。
如果我们将 [L[lx],R[lx]] 区间内出发的路径所对应的终点的权值扣回去之后 [L[rx],R[rx]] 的权值和扣回去之前不一样那么就存在一条可以直达的路径。
在写的时候,我们按dfs序跑一遍,一边跑一边修改。
我们在访问到L[lx]-1 和 R[lx]的时候分别求一下 [L[rx],R[rx]]的值,把它们相减判断一下就好了。
区间和拿个树状数组搞搞就好了。

代码

#include
#define N 200005
#define D 18
using namespace std;
int n,m,Q,f[N][D+1],w[N][D+1],g[N],sum[N],ans[N];
int k,la[N],ff[N*2],dep[N],L[N],R[N],cnt,c[N];
struct node{int a,b;}e[N*2];
vectorq[N];
vector<int>t[N],h[N];
void add(int a,int b)
{
  e[++k]=(node){a,b};ff[k]=la[a];la[a]=k;
  e[++k]=(node){b,a};ff[k]=la[b];la[b]=k;
}

class bit
{
  public:
  void modify(int x,int val)
  {
    for(;x<=n;x+=x&-x)c[x]+=val;
  }
  int qry(int x)
  {
    int res=0;
    for(;x;x-=x&-x)res+=c[x];
    return res;
  }
}T;

void dfs1(int x)
{
  L[x]=++cnt;
  for(int i=1;i<=D;i++)
    w[x][i]=w[w[x][i-1]][i-1];
  for(int a=la[x];a;a=ff[a])
    if(e[a].b!=w[x][0])
      dep[e[a].b]=dep[x]+1,dfs1(e[a].b);
  R[x]=cnt;
}

void dfs2(int x)
{
  g[x]=x;
  for(int i=0;ix].size();i++)
    if(dep[h[x][i]]x]])g[x]=h[x][i];
  for(int a=la[x];a;a=ff[a])
    if(e[a].b!=w[x][0])
    {
      dfs2(e[a].b);
      if(dep[g[e[a].b]]x]])g[x]=g[e[a].b];
    }
  f[x][0]=g[x];
}

void prepare()
{
  for(int j=1;j<=D;j++)
    for(int i=1;i<=n;i++)
      f[i][j]=f[f[i][j-1]][j-1];
}

int lca(int x,int y)
{
  if(dep[x]y])swap(x,y);
  for(int i=D;i>=0;i--)
    if(dep[w[x][i]]>=dep[y])x=w[x][i];
  if(x==y)return x;
  for(int i=D;i>=0;i--)
    if(w[x][i]!=w[y][i])x=w[x][i],y=w[y][i];
  return w[x][0];
}

node work(int x,int pos)
{
  if(x==pos)return (node){x,0};
  int res=0;
  for(int i=D;i>=0;i--)
    if(dep[f[x][i]]>dep[pos])x=f[x][i],res+=(1<if(f[x][0]==x)return (node){x,-1};
  return (node){x,res};
}

int main()
{
  int x,y,id,inv,pos;node A,B;
  scanf("%d",&n);
  for(int i=2;i<=n;i++)
    scanf("%d",&w[i][0]),add(w[i][0],i);
  dep[1]=1;dfs1(1);
  scanf("%d",&m);
  for(int i=1;i<=m;i++)
  {
    scanf("%d%d",&x,&y);pos=lca(x,y);
    if(L[x]>L[y])swap(x,y);
    t[L[x]].push_back(L[y]);
    if(dep[pos]x])h[x].push_back(pos);
    if(dep[pos]y])h[y].push_back(pos);
  }
  scanf("%d",&Q);dfs2(1);prepare();
  for(int i=1;i<=Q;i++)
  {
    scanf("%d%d",&x,&y);pos=lca(x,y);
    A=work(x,pos);B=work(y,pos);
    if(A.b==-1||B.b==-1){ans[i]=-1;continue;}
    if(x==pos||y==pos){ans[i]=A.b+B.b+1;continue;}
    ans[i]=A.b+B.b+2;x=A.a;y=B.a;
    if(L[x]>L[y])swap(x,y);
    q[L[x]-1].push_back((node){y,-i});
    q[R[x]].push_back((node){y,i}); 
  }
  for(int i=1;i<=n;i++)
  {
    for(int j=0;j1);
    for(int j=0;j<q[i].size();j++)
    {
      inv=(q[i][j].b>0?1:-1);
      x=q[i][j].a;id=q[i][j].b*inv;
      sum[id]+=inv*(T.qry(R[x])-T.qry(L[x]-1)); 
    }
  }
  for(int i=1;i<=Q;i++)
    printf("%d\n",ans[i]-(sum[i]>0));
  return 0;
} 

你可能感兴趣的:(题解,数据结构,——树状数组,贪心)