洛谷P1600 天天爱跑步(NOIp2016)(BZOJ4719)

来源:互联网 发布:网络优化工作美篇 编辑:程序博客网 时间:2024/05/19 19:31

LCA

洛谷题目传送门

BZOJ题目传送门

炒鸡难的一题。。。码量还不小。。。

只想到拆成链,然而不知道怎么实现。。。只好认怂去看题解。。。

考虑每条路径,拆成两个链。一个从s到LCA,另一个从LCA到t。如果LCA有贡献的话就把答案-1(不然就算重啦)。

然后在每个s打个1,在LCA上打个-1,那么就变成求一个节点的子树和。

对于点i,当depth[s]depth[i]=w[i]时产生贡献。那么令num[i]=w[i]+depth[i]。查询时更新num[i]即可。
对于另一条链类似,当depth[t]depth[i]=disw[i]时产生贡献。同样令num[i]=w[i]depth[i],不过这时disdepth[t]可能为负。因此需要把数组平移。

LCA的话我用的是Tarjan,时间复杂度O(n+m)

当然倍增也是可以的。

代码:

#include<cstdio>#include<cstring>#include<vector>#include<algorithm>#define MAXN 300000#define MAXM 600000using namespace std;struct edge{ int next,to; };struct route{ int s,t,lca,dis; };int n,m,k,md;int w[MAXN+5],h1[MAXN+5],h2[MAXN+5],fa[MAXN+5],depth[MAXN+5],num[MAXN+5],ans[MAXN+5],t[MAXN+5],tt[MAXM+5];edge ed[MAXM*2+5];route p[MAXN+5];vector <int> t1[MAXN+5],t2[MAXN+5],t3[MAXN+5];bool f[MAXN+5];inline char readc(){    static char buf[100000],*l=buf,*r=buf;    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);    if (l==r) return EOF; return *l++;}inline int _read(){    int num=0; char ch=readc();    while (ch<'0'||ch>'9') ch=readc();    while (ch>='0'&&ch<='9') { num=num*10+ch-48; ch=readc(); } return num;}void addedge(int x,int y,int *h){    ed[++k].next=h[x]; ed[k].to=y; h[x]=k;}int findfather(int x){    if (fa[x]==x) return x;    return fa[x]=findfather(fa[x]);}void Tarjan(int x,int father){    fa[x]=x; f[x]=true;    for (int i=h2[x];i;i=ed[i].next){        int v=ed[i].to;        if (x==p[v].s&&f[p[v].t]) p[v].lca=findfather(p[v].t);        if (x==p[v].t&&f[p[v].s]) p[v].lca=findfather(p[v].s);    }    for (int i=h1[x];i;i=ed[i].next)        if (ed[i].to!=father){            int v=ed[i].to; depth[v]=depth[x]+1; Tarjan(v,x); fa[v]=x;        }}void dfs1(int x,int father){    int now=w[x]+depth[x],l; if (now<=md) l=t[now];    for (int i=h1[x];i;i=ed[i].next)        if (ed[i].to!=father) dfs1(ed[i].to,x);    t[depth[x]]+=num[x]; if (now<=md) ans[x]=t[now]-l;    int len=t1[x].size();    for (int i=0;i<len;i++) t[depth[t1[x][i]]]--;}void dfs2(int x,int father){    int now=depth[x]-w[x]+MAXN,l; l=tt[now];    for (int i=h1[x];i;i=ed[i].next)        if (ed[i].to!=father) dfs2(ed[i].to,x);    int len=t2[x].size(); for (int i=0;i<len;i++) tt[MAXN+t2[x][i]]++;    len=t3[x].size(); ans[x]+=tt[now]-l;    for (int i=0;i<len;i++) tt[MAXN+t3[x][i]]--;}int main(){    n=_read(); m=_read();    for (int i=1;i<n;i++){        int u=_read(),v=_read(); addedge(u,v,h1); addedge(v,u,h1);    }    for (int i=1;i<=n;i++) w[i]=_read();    for (int i=1;i<=m;i++){        p[i].s=_read(); p[i].t=_read(); num[p[i].s]++; addedge(p[i].s,i,h2); addedge(p[i].t,i,h2);     }    Tarjan(1,0);    for (int i=1;i<=n;i++) md=max(md,depth[i]);    for (int i=1;i<=m;i++){        p[i].dis=depth[p[i].s]+depth[p[i].t]-depth[p[i].lca]*2;        t1[p[i].lca].push_back(p[i].s);    }    dfs1(1,0);    for (int i=1;i<=m;i++){        t2[p[i].t].push_back(depth[p[i].t]-p[i].dis);        t3[p[i].lca].push_back(depth[p[i].t]-p[i].dis);    }    dfs2(1,0);    for (int i=1;i<=m;i++) if (depth[p[i].s]-w[p[i].lca]==depth[p[i].lca]) ans[p[i].lca]--;    for (int i=1;i<=n;i++) printf("%d ",ans[i]);    return 0;}