Luogu P1600 天天爱跑步 树上差分

Luogu P1600 天天爱跑步

### 树上差分

题目链接
树上问题
没看出怎么差分
被观察到的条件有两个
lca前一半(包括lca) \(d[S_i]-d[x]=w[x]\)
\(d[i]\)表示节点深度
lca后一半 \(d[S_i]+d[x]-2*d[lca(S_i,T_i)]=w[x]\)
但是具体怎么实现这个公式??
实现 \(d[S_i]=w[x]+d[x]\)
可以转化为线段树合并模型
在\(S_i\)到\(lca\)的路径上增加\(d[S_i]\)的价值
最后求出各点的\(w[x]+d[x]\)的价值个数
但是好像有点开不下。
可以在每一个节点上建一个vector记录投放
然后一个全局数组c
在树上dfs的时候记录\(c[w[x]+d[x]]\)递归回来的时候和原来的做差就是答案。



代码如下:

#include<bits/stdc++.h>
#define mk make_pair
using namespace std;
const int maxn=300000;
int n,m,head[maxn],tot,w[maxn],lc[maxn],ans[maxn];
int id[maxn],d[maxn],fa[maxn],f[maxn],c1[maxn*2],c2[maxn*2];
struct node{
    int nxt,to;
    #define nxt(x) e[x].nxt
    #define to(x) e[x].to
}e[maxn<<1];
inline void add(int from,int to){
    to(++tot)=to;nxt(tot)=head[from];head[from]=tot;
}
inline int find(int x){return fa[x]==x ? fa[x] : fa[x]=find(fa[x]);}
vector<pair<int,int> > pt[maxn];
vector<int> a1[maxn],a2[maxn],b1[maxn],b2[maxn];
pair<int,int> di[maxn];
void tarjan(int x){
    id[x]=1;
    for(int i=head[x];i;i=nxt(i)){
        if(id[to(i)]) continue;
        d[to(i)]=d[x]+1;
        tarjan(to(i));
        fa[to(i)]=x;f[to(i)]=x;
    }
    for(unsigned int i=0;i<pt[x].size();i++){
        int to=pt[x][i].first,vl=pt[x][i].second;
        if(id[to]==2) lc[vl]=find(to);
    }
    id[x]=2;
}
void dfs(int x){
    int val1=c1[d[x]+w[x]],val2=c2[w[x]-d[x]+n];
    id[x]=1;
    for(int i=head[x];i;i=nxt(i)){
        if(id[to(i)]) continue;
        dfs(to(i));
    }
    for(int i=0;i<a1[x].size();i++)
        c1[a1[x][i]]++;
    for(int i=0;i<b1[x].size();i++)
        c1[b1[x][i]]--;
    for(int i=0;i<a2[x].size();i++)
        c2[n+a2[x][i]]++;
    for(int i=0;i<b2[x].size();i++)
        c2[n+b2[x][i]]--;
    ans[x]=c1[d[x]+w[x]]+c2[w[x]-d[x]+n]-val1-val2;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++){
        int x,y;scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    for(int i=1;i<=n;i++) fa[i]=i,scanf("%d",&w[i]);
    for(int i=1;i<=m;i++){
        int x,y;scanf("%d%d",&x,&y);
        if(x==y) lc[i]=x;
        pt[x].push_back(mk(y,i));pt[y].push_back(mk(x,i));
        di[i].first=x;di[i].second=y;
    }
    d[1]=1;tarjan(1);
    for(int i=1;i<=m;i++){
        int x=di[i].first,y=di[i].second;
        a1[x].push_back(d[x]);a2[y].push_back(d[x]-2*d[lc[i]]);
        b1[f[lc[i]]].push_back(d[x]);b2[lc[i]].push_back(d[x]-2*d[lc[i]]);
    }
    memset(id,0,sizeof(id));
    dfs(1);
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/ChrisKKK/p/11619779.html

时间: 2024-10-08 12:25:21

Luogu P1600 天天爱跑步 树上差分的相关文章

luogu P1600 天天爱跑步 |树上差分+LCA

题目描述 小c 同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含 nnn 个结点和 n?1n-1n?1 条边的树,每条边连接两个结点,且任意两个结点存在一条路径互相可达.树上结点编号为从 111 到 nnn 的连续正整数. 现在有 mmm 个玩家,第 iii 个玩家的起点为 sis_isi?,终点为 tit_iti?.每天打卡任务开始时,所有玩家在第 000 秒同时

[luogu]P1600 天天爱跑步[LCA]

[luogu]P1600 [NOIP 2016]天天爱跑步 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含n个结点和n−1条边的树, 每条边连接两个结点,且任意两个结点存在一条路径互相可达.树上结点编号为从1到n的连续正整数. 现在有m个玩家,第i个玩家的起点为Si?,终点为Ti? .每天打卡任务开始时,所有玩家在第0秒同时从自己的起点出发, 以每秒跑一条边的速

bzoj4719: [Noip2016]天天爱跑步 树上差分

Description 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.?天天爱跑步?是一个养成类游戏,需要 玩家每天按时上线,完成打卡任务.这个游戏的地图可以看作一一棵包含 N个结点和N-1 条边的树, 每条边连接两 个结点,且任意两个结点存在一条路径互相可达.树上结点编号为从1到N的连续正整数.现在有个玩家,第个玩家的 起点为Si ,终点为Ti .每天打卡任务开始时,所有玩家在第0秒同时从自己的起点出发, 以每秒跑一条边的速度, 不间断地沿着最短路径向着自己的终点跑去,

BZOJ 4719--天天爱跑步(LCA&amp;差分)

4719: [Noip2016]天天爱跑步 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 1464  Solved: 490[Submit][Status][Discuss] Description 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.?天天爱跑步?是一个养成类游戏,需要 玩家每天按时上线,完成打卡任务.这个游戏的地图可以看作一一棵包含 N个结点和N-1 条边的树, 每条边连接两 个结点,且任意两个结点存在一条路

P1600 天天爱跑步

lca真心不太会,这里只介绍60分做法,100的太难辣简单了就不介绍了 n<=1000 zz回溯爆搜 S[i]全部相等 这dfs序都不用lca的,2333,差分,然后输出判断一下是否是0(1到i的时间是固定的) 退化成一条链子 一个点i的ans就是i-time[i]和i+tim[i]的起点个数(当然要合法啦) 乱搞就好了,这里写的nlogn的 #include <iostream> #include <queue> #include <cstdio> #inclu

【NOIP2016】天天爱跑步(树上差分)

题意: 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.?天天爱跑步?是一个养成类游戏,需要 玩家每天按时上线,完成打卡任务.这个游戏的地图可以看作一一棵包含 N个结点和N-1 条边的树, 每条边连接两 个结点,且任意两个结点存在一条路径互相可达.树上结点编号为从1到N的连续正整数.现在有个玩家,第个玩家的 起点为Si ,终点为Ti  .每天打卡任务开始时,所有玩家在第0秒同时从自己的起点出发, 以每秒跑一条边的速度, 不间断地沿着最短路径向着自己的终点跑去, 跑到终点后该

Noip 2016 天天爱跑步 【树上倍增+深搜】

题目: 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.?天天爱跑步?是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含 个结点和 条边的树, 每条边连接两个结点,且任意两个结点存在一条路径互相可达.树上结点编号为从到的连续正整数. 现在有个玩家,第个玩家的起点为 ,终点为 .每天打卡任务开始时,所有玩家在第秒同时从自己的起点出发, 以每秒跑一条边的速度, 不间断地沿着最短路径向着自己的终点跑去, 跑到终点后该玩家就算完成了打卡任务.

[树上差分][lca] Luogu P3258 松鼠的新家

题目描述 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他居然真的住在”树“上. 松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的指南顺序,先去a1,再去a2,......,最后到an,去参观新家.可是这样会导致维尼重复走很多房间,懒惰的维尼不停地推辞.可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃. 维尼是个馋家伙,立马就答应了.现在松鼠希望知道为了保证维尼有

noip 2016 天天爱跑步

描述 小C同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一棵包含n个结点和n - 1条边的树,每条边连接两个结点,且任意两个结点存在一条路径互相可达.树上结点编号为从1到n的连续正整数. 现在有m个玩家,第i个玩家的起点为Si,终点为Ti.每天打卡任务开始时,所有玩家 在第0秒 同时从 自己的起点 出发,以 每秒跑一条边 的速度,不间断地沿着最短路径向着 自己的终点 跑去,