NOIP2016Day1T2天天爱跑步(LCA+桶)

  据说是今年NOIP最难一题了。。。我还记得当时满怀期待心情点开Day1的题发现T2就不会了于是怀疑人生良久。。。

  啊好像很多大爷都是用线段树合并写的,我怎么什么数据结构都不会啊呜呜呜。。。

  题目大意就不说了QWQ

  网上的O(n)说法说得好简略啊,我理解了好久。。。可能是我太弱了QAQ这方面的题做得也少

  首先我们可以随手推出某个点i可以观测到一个从x跑到y的玩家的话,肯定有d[i]+w[i]=d[x]或者d[i]-w[i]=d[y]-dis【dis为x到y的距离

  所以肯定要先求LCA辣,自从转了C++,写tarjan lca只要3行,比pascal的tarjan高到不知道哪里去了,还比倍增快OWO

  O(n)的做法比较繁琐。我们需要开一个i点向下能到达的起点的桶cntq[i]和一个i点向下能到达的终点的桶cntz[i]。dfs搜到某个点的时候,cntq[d[x]]+=cnt[x]【cnt[x]表示这个点是起点的个数】,然后对于这个点上的每一个终点cntz[d[y]-dis]++。这样的话某个点i的答案ans[i]=cntq[d[i]+w[i]]+cntz[d[i]-w[i]]。

  当起点和终点的lca出栈的时候,要把跟这两个点有关cnts和cntx减掉,因为它们对答案不再有贡献。

  但是我们会发现,对于某个点i,其他子树的起点x或终点y【lca(x,y)还未退栈】有可能会被误算贡献【因为开的桶的下标是深度】,所以我们在进入某棵子树之前,先把答案ans[i]计算一次,回溯之后再计算一次答案ans[i],两个答案相减就是这棵子树的答案了。

  还要注意如果i是lca(x,y)的话答案会算重复,需要减掉一个。。。

  然后这题就又写完辣【BZOJ上要求的格式太坑了。。。PE了好多次QAQ

代码如下:

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
using namespace std;
const int maxn=300010;
struct poi{int too,pre,pos;}e[maxn*2],e3[maxn*2],e4[maxn*2],e5[maxn*2],lik[maxn*2];
int n,m,tot1,tot2,tot3,tot4,tot5,ans[maxn],fa[maxn],cntq[maxn*2],cntz[maxn*2],d[maxn],w[maxn],cnt[maxn];
int x,y,tim,elast[maxn*2],llast[maxn*2],e3last[maxn*2],e4last[maxn*2],e5last[maxn*2],q[maxn*2][4];
bool v[maxn];
int gf(int x){return fa[x]==x?x:fa[x]=gf(fa[x]);}
void add1(int x,int y){e[++tot1].too=y;e[tot1].pre=elast[x];elast[x]=tot1;}
void add2(int x,int y,int z){lik[++tot2].too=y;lik[tot2].pos=z;lik[tot2].pre=llast[x];llast[x]=tot2;}
void add3(int x,int y){e3[++tot3].too=y;e3[tot3].pre=e3last[x];e3last[x]=tot3;}
void add4(int x,int y){e4[++tot4].too=y;e4[tot4].pre=e4last[x];e4last[x]=tot4;}
void add5(int x,int y){e5[++tot5].too=y;e5[tot5].pre=e5last[x];e5last[x]=tot5;}
void tarjan(int x)
{
    fa[x]=x;v[x]=1;
    for(int i=llast[x];i;i=lik[i].pre)if(v[lik[i].too])q[lik[i].pos][3]=gf(lik[i].too);
    for(int i=elast[x],too=e[i].too;i;i=e[i].pre,too=e[i].too)if(!v[too])d[too]=d[x]+1,tarjan(too),fa[too]=x;
}
void dfs(int x,int fa)
{
    int lx=cntq[d[x]+w[x]],ly=cntz[d[x]-w[x]+maxn];cntq[d[x]]+=cnt[x];
    for(int i=e3last[x];i;i=e3[i].pre)cntz[e3[i].too]++;
    for(int i=elast[x];i;i=e[i].pre){if(e[i].too!=fa)dfs(e[i].too,x);}
    ans[x]=cntq[d[x]+w[x]]+cntz[d[x]-w[x]+maxn]-lx-ly;
    for(int i=e4last[x];i;i=e4[i].pre){cntq[e4[i].too]--;if(e4[i].too==d[x]+w[x])ans[x]--;}
    for(int i=e5last[x];i;i=e5[i].pre)cntz[e5[i].too]--;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)scanf("%d%d",&x,&y),add1(x,y),add1(y,x);
    for(int i=1;i<=n;i++)scanf("%d",&w[i]);
    for(int i=1;i<=m;i++)scanf("%d%d",&x,&y),q[i][1]=x,q[i][2]=y,add2(x,y,i),add2(y,x,i),cnt[x]++;
    tarjan(1);
    for(int i=1;i<=m;i++)
    {
        int dis=d[q[i][1]]+d[q[i][2]]-2*d[q[i][3]];
        add3(q[i][2],d[q[i][2]]-dis+maxn);
        add4(q[i][3],d[q[i][1]]);
        add5(q[i][3],d[q[i][2]]-dis+maxn);
    }
    dfs(1,0);
    for(int i=1;i<n;i++)printf("%d ",ans[i]);
    printf("%d",ans[n]);
}

时间: 2024-10-27 05:22:01

NOIP2016Day1T2天天爱跑步(LCA+桶)的相关文章

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

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

[NOIP2016-day1-T2]天天爱跑步running_题解

题目来源:http://www.lydsy.com/JudgeOnline/problem.php?id=4719 镇楼图: noip滚粗后..订正的第一题. 题目大意: 有若干条路径在一棵树上,问每个点恰为多少条路径起点出发Ti长度处. 解题方略: 这题可以O(n)..结果shy非常有趣地在考场上码80分暴力.结果还爆QAQ(这题,80分做法,比100分做法难吧..) 考虑把询问分成不同的两个链.但是,如果有链的话,就不可避免要树剖(就是因为他不像联赛算法,shy考试时才没写QWQ).然而其实

天天爱跑步:桶(就是数组)/权值线段树(没打)

提取:等式转换,桶,倍增lca 对于(x,y)的一次提问,我们规定lca为(x,y)的lca d为深度,w为点出现观察员的时间 那么对于(x,lca)这段路径上的点i,此次提问能作出贡献的等式是 d[x]-d[i]=w[i] ->d[x]=w[i]+d[i] 对于(lca,y)这段路径上的点i,此次提问能作出贡献的等式是 d[x]-d[lca]+d[i]-d[lca]=w[i] ->d[x]-2*d[lca]=w[i]-d[i] 那么我们可以将提答转化为区间修改了! 在(x,lca)上将“d[

cogs2557 天天爱跑步 LCA

从去年$11$月填到现在的超级天坑--链接:http://cogs.pro/cogs/problem/problem.php?pid=2557 题意--给出一棵树,上面有一堆链,求出每个节点在指定的时刻有几条链恰好到达-- 联赛超纲的开始-- 详细叙述一下从去年十一月到现在的心路历程-- $2016.11$现场:卧槽这题一脸不可做啊-- $2016.12$:这个题出错地方了吧-- $2017.1$:什么玩意--好像是图上的吧-- $2017.2$(此时刚刚学习树):我好像会点了--(会什么?)如

LCA+线段树 NOIP2016 天天爱跑步

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

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

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

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

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

天天爱跑步[NOIP2016]

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