[NOIP2016] 天天爱跑步

题意:

传送门

题解:

去年D1T2,呵呵,被pei死,菜鸡的我考场上第一次做图论题,愉快爆零;

链剖+转化等式;

链剖就是求个lca和路径长度;

首先考虑链的情况:

先假设s在t的上面,那么对于一个点i,能对它产生贡献的路径一定满足i-s==w[i] && t>=i,于是我们设k[i]=i-w[i],cnt[i]表示以i为起点的路径条数,那么从上往下遍历,到点i先计算以点i为起点的贡献,再计算点i的答案,即访问cnt[k[i]],注意到t>=i,所以到i后cnt[以i为终点的起点]--,这里用vector存一下即可;

对于s在t下面的类似,只是k[i]=deep[i]+w[i];

正解和链很类似:

考虑将一条路径划分为两条链:s->lca,t->lca,然后我们就可以用类似于链的做法;

对于s->lca的一条链,我们只考虑s->t这条路径对s->lca这条链上的点的贡献,则对i有贡献需满足deep[s]-deep[i]w[i],移项之后又有k[i]=deep[i]+w[i]deep[s],于是在s上打+1标记,在lca上打-1标记 ,选1为根dfs,对每个点统计一下答案即可,但是有一点需要注意,与链的情况不同的是,这里的deep[s]是s的深度,可能会把别的子树的贡献计入答案,于是我们进入这个点时记一下这个点cnt[k[i]]的last,然后回溯后cnt[k[i]]-last就是对这个点产生的贡献。

t->lca的情况类似,只是k[i]=deep[i]-w[i]==deep[t]-len(len为路径长度)。

总结:对某个等式进行分析时,对等式进行化归,相关联的变量可划到等式的一边,从而找出各变量之间的线性关系。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define ll long long
#define N 300010
#define M 300000
using namespace std;

int n,m,e_num,deep;
int nxt[N<<1],to[N<<1],h[N];
int fa[N],dep[N],top[N],siz[N],son[N],w[N],val[N],ans[N],cnt[N<<1];

struct Node {int s,t,lca,len;}p[N];

vector<int> v1[N],v2[N],v3[N];

int gi() {
  int x=0,o=1; char ch=getchar();
  while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
  if(ch=='-') o=-1,ch=getchar();
  while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
  return o*x;
}

void add(int x, int y) {
  nxt[++e_num]=h[x],to[e_num]=y,h[x]=e_num;
}

void dfs1(int u) {
  siz[u]=1;
  for(int i=h[u]; i; i=nxt[i]) {
    int v=to[i];
    if(v==fa[u]) continue;
    fa[v]=u,dep[v]=dep[u]+1;
    dfs1(v);
    if(siz[v]>siz[son[u]]) son[u]=v;
    siz[u]+=siz[v];
  }
}

void dfs2(int u) {
  if(son[u]) top[son[u]]=top[u],dfs2(son[u]);
  for(int i=h[u]; i; i=nxt[i]) {
    int v=to[i];
    if(v==fa[u] || v==son[u]) continue;
    top[v]=v,dfs2(v);
  }
}

int lca(int x, int y) {
  while(top[x]!=top[y]) {
    if(dep[top[x]]>dep[top[y]]) x=fa[top[x]];
    else y=fa[top[y]];//hehe,chain divide was wrong
  }
  if(dep[x]<dep[y]) return x;
  else return y;
}

void sol1(int u) { 
  int last,num=dep[u]+w[u];
  if(num<=deep) last=cnt[num];
  for(int i=h[u]; i; i=nxt[i]) {
    if(to[i]!=fa[u]) sol1(to[i]);
  }
  cnt[dep[u]]+=val[u];
  if(num<=deep) ans[u]+=cnt[num]-last;
  for(int i=0; i<v1[u].size(); i++) cnt[v1[u][i]]--;
}

void sol2(int u) {
  int last,num=dep[u]-w[u]+M;
  last=cnt[num];
  for(int i=h[u]; i; i=nxt[i]) {
    if(to[i]!=fa[u]) sol2(to[i]);
  }
  for(int i=0; i<v2[u].size(); i++) cnt[v2[u][i]+M]++;
  ans[u]+=cnt[num]-last;
  for(int i=0; i<v3[u].size(); i++) cnt[v3[u][i]+M]--;
}

int main() {
  n=gi(),m=gi();
  for(int i=1; i<n; i++) {
    int x=gi(),y=gi();
    add(x,y),add(y,x);
  }
  fa[1]=1,dep[1]=1,top[1]=1;
  dfs1(1),dfs2(1);
  for(int i=1; i<=n; i++) w[i]=gi(),deep=max(deep,dep[i]);
  for(int i=1; i<=m; i++) {
    p[i].s=gi(),p[i].t=gi();
    p[i].lca=lca(p[i].s,p[i].t);
    p[i].len=dep[p[i].s]+dep[p[i].t]-2*dep[p[i].lca];    
    val[p[i].s]++;
    v1[p[i].lca].push_back(dep[p[i].s]);
    v2[p[i].t].push_back(dep[p[i].t]-p[i].len);
    v3[p[i].lca].push_back(dep[p[i].t]-p[i].len);
  }
  sol1(1);
  memset(cnt,0,sizeof(cnt));
  sol2(1);
  for(int i=1; i<=m; i++) {
    if(dep[p[i].s]==dep[p[i].lca]+w[p[i].lca]) ans[p[i].lca]--;
  }
  for(int i=1; i<=n; i++) printf("%d ", ans[i]);
  return 0;
}

附上部分分的代码,没有暴力跳路径和T=1的点

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define N 300010
using namespace std;

int n,m,e_num;
int nxt[N<<1],to[N<<1],h[N],siz[N],dep[N],w[N],cnt[N],inx[N],v1[N],v2[100010][100],res[N];
bool flg1=1,flg2=1,flg3=1,flg4=1,flg5=1;

struct Node {int s,t;}p[N];

int gi() {
  int x=0,o=1; char ch=getchar();
  while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
  if(ch=='-') o=-1,ch=getchar();
  while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
  return o*x;
}

void add(int x, int y) {
  nxt[++e_num]=h[x],to[e_num]=y,h[x]=e_num;
}

void dfs(int u, int fa) {
  for(int i=h[u]; i; i=nxt[i]) {
    int v=to[i];
    if(v==fa) continue;
    dep[v]=dep[u]+1;
    dfs(v,u);
    siz[u]+=siz[v];
  }
}

void work1() {
  for(int i=1; i<=m; i++) cnt[p[i].s]++;
  for(int i=1; i<=n; i++) {
    if(!w[i]) printf("%d ", cnt[i]);
    else printf("0 ");
  }
}

void work2() {
  for(int i=1; i<=m; i++) cnt[p[i].s]++;
  for(int i=1; i<=n; i++) {
    printf("%d ", cnt[i]);
  }
}

void work3() {
  for(int i=1; i<=m; i++) {
    if(p[i].s<=p[i].t) {
      v1[p[i].s]++,v2[p[i].t][++v2[p[i].t][0]]=p[i].s;
    }
  }
  for(int i=1; i<=n; i++) {
    cnt[i]+=v1[i];
    if(i-w[i]>=1) res[i]+=cnt[i-w[i]];
    for(int j=1; j<=v2[i][0]; j++) cnt[v2[i][j]]--;
  }
  memset(v1,0,sizeof(v1));
  memset(v2,0,sizeof(v2));
  memset(cnt,0,sizeof(cnt));
  for(int i=1; i<=m; i++) {
    if(p[i].s>p[i].t)
      v1[p[i].s]++,v2[p[i].t][++v2[p[i].t][0]]=p[i].s;
  }
  for(int i=n; i>=1; i--) {
    cnt[i]+=v1[i];
    if(i+w[i]<=n) res[i]+=cnt[i+w[i]];
    for(int j=1; j<=v2[i][0]; j++) cnt[v2[i][j]]--;
  }
  for(int i=1; i<=n; i++)
    printf("%d ", res[i]);
}

void work4() {
  for(int i=1; i<=m; i++) siz[p[i].t]++;
  dfs(1,0);
  for(int i=1; i<=n; i++) {
    if(dep[i]==w[i]) cnt[i]=siz[i];
    printf("%d ", cnt[i]);
  }
}

int main() {
  n=gi(),m=gi();
  for(int i=1; i<n; i++) {
    int x=gi(),y=gi();
    add(x,y),add(y,x);
    inx[x]++,inx[y]++;
    if(inx[x]>2 || inx[y]>2) flg3=0;
  }
  for(int i=1; i<=n; i++) {
    w[i]=gi();
    if(w[i]) flg2=0;
  }
  for(int i=1; i<=m; i++) {
    int s=gi(),t=gi();
    if(s!=t) flg1=0;
    if(s!=1) flg4=0;
    if(t!=1) flg5=0;
    p[i]=(Node){s,t};
  }
  if(flg1) work1();
  if(flg2) work2();
  if(flg3) work3();
  if(flg4) work4();
  return 0;
}
posted @ 2017-10-23 16:42  HLX_Y  阅读(193)  评论(0编辑  收藏  举报