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

292 篇文章 1 订阅
281 篇文章 1 订阅

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 600000
using 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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值