bzoj 3631 [JLOI2014]松鼠的新家

2 篇文章 0 订阅
1 篇文章 0 订阅

题目

传送门

题解

看到某Yuta的树总结觉得LCA+树上差分挺好写而且差分没写过准备水一发结果水了一上午qwq。因为第一次写树上差分和树剖求LCA,结果树剖版子某处打错了。。(而且差分点和差分边还是有一定区别的)。


这题可以用树剖水部分分,不过n有30w,log方就会被卡了。所以利用差分思想把区间修改转化为点修改。给每个点一个tag表示新增值(貌似怪怪的)。如果有条路径(i,j)需要标记那么进行如下操作

tag(i)++;tag(j)++;tag(LCA(i,j))- -;tag(father(LCA(i,j)))- -;

这样能够达到标记经过的点的效果,请自行脑补如果是经过的边如何标记。
那么就把路径拆分就好了,但是这道题非起始端点会被多算一次(在衔接处),所以统计完后减回来。


题很水,人很蠢。

代码

//QWsin
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=600000+10;
const int maxm=1200000+10;
inline int read()
{
    int ret=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) ret=ret*10+ch-'0';
    return ret;
}

int to[maxn];

int first[maxn],next[maxm],ecnt;
struct Edge{int u,v;Edge(int u=0,int v=0):u(u),v(v){}}e[maxm];
inline void add_edge(int u,int v)
{
    next[ecnt]=first[u];first[u]=ecnt;e[ecnt++]=Edge(u,v);
    next[ecnt]=first[v];first[v]=ecnt;e[ecnt++]=Edge(v,u);
}

int top[maxn],hv[maxn],sz[maxn],p[maxn],dep[maxn];
int tag[maxn];  

int dfs1(int u,int fa,int deep=1)
{
    p[u]=fa;dep[u]=deep;sz[u]=1;int msz=0;
    for(int i=first[u];i!=-1;i=next[i]) if(e[i].v!=fa){
        int lch=dfs1(e[i].v,u,deep+1);
        if(lch > msz) msz=lch,hv[u]=e[i].v;
        sz[u]+=lch;
    }
    return sz[u];
}

void dfs2(int u,int Top)
{
    top[u]=Top;
    if(sz[u]==1) return ;
    dfs2(hv[u],Top);
    for(int i=first[u];i!=-1;i=next[i])
        if(e[i].v!=p[u]&&e[i].v!=hv[u]) dfs2(e[i].v,e[i].v);
}

int LCA(int a,int b)
{
    for(;top[a]!=top[b];b=p[top[b]])
        if(dep[top[a]] > dep[top[b]]) swap(a,b);
    return dep[a]<dep[b]?a:b;
}

int ans[maxn],sum,n;
int dfs3(int cur)
{
    int sum=tag[cur];
    for(int i=first[cur];i!=-1;i=next[i]) 
        if(e[i].v!=p[cur]) sum+=dfs3(e[i].v);
    if(to[1]!=cur) ans[cur]=sum-1;
    else ans[cur]=sum; return sum;
}

int main()
{
    n=read();memset(first,-1,sizeof(first));
    for(int i=1;i<=n;i++) to[i]=read();
    for(int i=1;i<n;i++) add_edge(read(),read());

    int root=1;
    dfs1(root,root);dfs2(root,root);

    tag[to[1]]++;tag[to[2]]++;
    int lca=LCA(to[1],to[2]);
    tag[lca]--;if(lca!=root) tag[p[lca]]--;

    for(int i=2;i<n;i++)
    {
        int lca=LCA(to[i],to[i+1]);
        tag[to[i]]++;tag[to[i+1]]++;
        tag[lca]--;if(lca!=root) tag[p[lca]]--;
    }

    dfs3(root);
    for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值