[NOIP2016]天天爱跑步

description

洛谷

data range

\[n\le 50 \]

solution

相信几乎机房里的所有人都已经切掉了。。。
蒟蒻直到\(NOIP2018\)前夕才草草补上。。。

把路径\(u->v\)拆成\(u->lca_{u,v}\)\(lca_{u,v}->v\)两部分,
\(dep_u\)为深度,\(tim_u\)为到达节点的时间戳,
\(u->lca_{u,v}\)这部分满足\(tim_x+dep[x]\)一定,\(lca_{u,v}->v\)这部分满足\(tim_x-dep[x]\)一定,
不再赘述。

此题的另一个难点在于如何利用差分统计答案。
如果直接对一个节点打标记然后开全局\(sum\)统计的话,相当于对一棵子树打标记,而我们要打的是链标记,这样显然是不对的。
考虑我们需要打标记的链上节点,发现链头和链尾一定有且仅有一个在子树中
于是我们把贡献转换为进入树节点和离开树节点时对应全局标记的差
代码如下

void dfs(int u,int ff){
    RG int cur=sum[w[u]];
    for(RG int i=head[u];i;i=nxt[i]){
        RG int v=to[i];if(v==ff)continue;
        dfs(v,u);
    }
    ans[u]+=sum[w[u]]-cur;
}

这样我们就能高效地维护差分信息,去掉求\(lca\)的复杂度,总复杂度为\(O(n)\)

然后说一下本蒟蒻在维护该信息的另一种略丑的方法
有了\(tim_x+dep[x]\)一定或\(tim_x-dep[x]\)一定的信息,一条链是肯定知道怎么做的
于是我们考虑把树重链剖分,依次考虑每一条重链,那么这样就可以直接转换为链上的问题来做
具体来说,差分时需要自底向上/自顶向下对每一条链进行差分,由于这样只会有\(logn\)条重链,因此这样做是\(O(mlogn)\)
最后统计答案时我们一条重链一条重链地统计,由于之前差分了\(O(mlogn)\)次,所有重链的长度之和为\(O(n)\),因此这样做是\(O(n+mlogn)\)
加上求\(lca\)的复杂度,总复杂度为\(O((n+m)logn)\),比标解略丑

Code

#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<iomanip>
#include<cstring>
#include<complex>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<ctime>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define F "4719"
#define mp make_pair
#define pb push_back
#define RG register
#define il inline
using namespace std;
typedef unsigned long long ull;
typedef pair<int,int> PI;
typedef vector<int>VI;
typedef long long ll;
typedef double dd;
const int N=3e5+10;
const int M=3e5+10;
const int K=3e5+10;
const int mod=998244353;
const int inf=2147483647;
const ll INF=1ll<<60;
const dd eps=1e-7;
const dd pi=acos(-1);
il ll read(){
  RG ll data=0,w=1;RG char ch=getchar();
  while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
  if(ch=='-')w=-1,ch=getchar();
  while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
  return data*w;
}

il void file(){
  freopen(F".in","r",stdin);
  freopen(F".out","w",stdout);
}

int n,m,w[N];
int head[N],nxt[N<<1],to[N<<1],cnt;
il void add(int u,int v){to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;}
int fa[N],sz[N],son[N],dep[N],top[N];int cal[N],cov;VI line[N];
void dfs1(int u,int ff){
  sz[u]=1;fa[u]=ff;son[u]=0;dep[u]=dep[ff]+1;
  for(RG int i=head[u];i;i=nxt[i]){
    RG int v=to[i];if(v==ff)continue;
    dfs1(v,u);sz[u]+=sz[v];if(sz[son[u]]<sz[v])son[u]=v;
  }
}
void dfs2(int u,int tp){
  line[tp].pb(u);top[u]=tp;if(son[u])dfs2(son[u],tp);else cal[++cov]=u;
  for(RG int i=head[u];i;i=nxt[i]){
    RG int v=to[i];if(v==fa[u]||v==son[u])continue;dfs2(v,v);
  }
}
il int lca(int u,int v){
  while(top[u]!=top[v])dep[top[u]]>dep[top[v]]?u=fa[top[u]]:v=fa[top[v]];
  return dep[u]<dep[v]?u:v;
}

VI C[2][N],D[2][N];int sum[2][N<<1],ans[N];
il void cover(int s,int t){
  RG int d,L=lca(s,t),k1=dep[s],k2=dep[s]-2*dep[L];
  if(w[L]==dep[s]-dep[L])ans[L]++;
  while(top[s]!=top[L]){C[0][s].pb(k1);D[0][top[s]].pb(k1);s=fa[top[s]];}
  if(s!=L){C[0][s].pb(k1);D[0][son[L]].pb(k1);}
  while(top[t]!=top[L]){C[1][t].pb(k2);D[1][top[t]].pb(k2);t=fa[top[t]];}
  if(t!=L){C[1][t].pb(k2);D[1][son[L]].pb(k2);}
}

int main()
{
  n=read();m=read();
  for(RG int i=1,u,v;i<n;i++){u=read();v=read();add(u,v);add(v,u);}
  for(RG int i=1;i<=n;i++)w[i]=read();dfs1(1,0);dfs2(1,1);
  for(RG int i=1,s,t;i<=m;i++){s=read();t=read();cover(s,t);}
  for(RG int x=1;x<=cov;x++){
    for(RG int u=cal[x];;u=fa[u]){
      for(RG int i=0,sz=C[0][u].size();i<sz;i++)sum[0][N+C[0][u][i]]++;
      for(RG int i=0,sz=C[1][u].size();i<sz;i++)sum[1][N+C[1][u][i]]++;
      ans[u]+=sum[0][N+w[u]+dep[u]]+sum[1][N+w[u]-dep[u]];
      for(RG int i=0,sz=D[0][u].size();i<sz;i++)sum[0][N+D[0][u][i]]--;
      for(RG int i=0,sz=D[1][u].size();i<sz;i++)sum[1][N+D[1][u][i]]--;
      if(top[u]==u)break;
    }
  }
  for(RG int i=1;i<=n;i++)printf("%d ",ans[i]);return 0;
}

posted @ 2018-10-11 22:23  cjfdf  阅读(193)  评论(0编辑  收藏  举报