题解 P3258 【[JLOI2014]松鼠的新家】(From luoguBlog)

唯一能得分的题也被自己搞炸了,好的。

考场上读完题基本认定和lca脱不了干系,想了一会确认是树剖。

那么问题来了,考前一节课刚发现自己之前打的树剖是错的。

而且就算是错的我也没信心考场调出来。

于是打了个自认为复杂度不高的暴力,T出天际,被各位不屑于打树剖的巨佬の向上标记干趴下。

考完翻书学了一下树上差分,赶脚不错。

树剖||Tarjan求lca+树上差分

后者的思路是预处理路线上每两个房间的lca

每次num[x]++,num[y]++,num[lca]++,num[father[lca]]++

最后进行一次dfs统计所有点的权值

int dfs(int x)
{
    v[x]=1;int now=0;
    for(int i=head[x];i;i=nxt[i])
    {
        int y=to[i];
        if(v[y])continue;
        now+=dfs(y);
    }
    num[x]+=now;
    return num[x];
}

最后num[way[1]]++
然后输出way[i]-1 (因为除了第一个点之外的每个点都算了两遍)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int N=300010;
int n;
int to[N<<1],nxt[N<<1],num[N],tot=0,fa[N],v[N],root,head[N],ans[N],way[N],father[N];
vector<int> q[N],qi[N];
void add(int x,int y)
{
    to[++tot]=y;
    nxt[tot]=head[x];
    head[x]=tot;
}
void insq(int x,int y,int i)
{
    q[x].push_back(y);
    qi[x].push_back(i);
    q[y].push_back(x);
    qi[y].push_back(i);
}
int getf(int x)
{
    if(x==fa[x])return x;
    fa[x]=getf(fa[x]);
    return fa[x];
}
void tarjan(int x)
{
    v[x]=1;
    for(int i=head[x];i;i=nxt[i])
    {
        int y=to[i];
        if(v[y])continue;
        tarjan(y);
        fa[y]=father[y]=x;
    }
    for(int i=0;i<q[x].size();i++)
    {
        int y=q[x][i],id=qi[x][i];
        if(v[y]==2)ans[id]=getf(y);
    }
    v[x]=2;
}
int dfs(int x)
{
    v[x]=1;int now=0;
    for(int i=head[x];i;i=nxt[i])
    {
        int y=to[i];
        if(v[y])continue;
        now+=dfs(y);
    }
    num[x]+=now;
    return num[x];
}
int main()
{
    /*freopen("FU.in","r",stdin);
    freopen("FU.out","w",stdout);*/
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&way[i]),fa[i]=i;
    root=way[1];
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
        x=way[i],y=way[i+1];
        insq(x,y,i);
    }
    /*cout<<endl;
    for(int i=1;i<=n;i++)cout<<father[i]<<' ';
    cout<<endl;*/
    tarjan(root);
    memset(v,0,sizeof(v));
    for(int i=1;i<n;i++)
    {
        int x=way[i],y=way[i+1],lca=ans[i];
        num[x]++;num[y]++;num[lca]--;num[father[lca]]--;
    }
    dfs(root);
    num[root]++;
    for(int i=1;i<=n;i++)printf("%d\n",num[i]-1);
    return 0;
}

转载于:https://www.cnblogs.com/Rorschach-XR/p/10969181.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值