[luogu1600]NOIp2016D1T2 天天爱跑步
题目链接:
luogu1600
谨以此题纪念那段年少无知但充满趣味的恬淡时光
附上一位dalao的博客链接:https://www.luogu.org/blog/user26242/ke-pa-di-tian-tian-ai-pao-bu
我写这道题的时候脑袋里一直想的是他还没AFO的时候的那段日子,真是快乐啊,虽然我那时什么都不会
好了废话结束
他写的是最为常见的差分+\(LCA\)+桶的写法,在此不再赘述
其实这是一道线段树合并的基础题
常规套路:我们将路径\((s,t)\)拆成\((s,lca)\)和\((lca,t)\),在这两段路径中
1)如果在\((s,lca)\)上的一点\(i\)看到,则\(w_i=dep_s-dep_i\),即\(w_i+dep_i=dep_s\)
2)如果在\((lca,t)\)上的一点\(i\)看到,则\(w_i=dep_s-dep_{lca}+dep_i-dep_{lca}\),即\(w_i-dep_i=dep_s-dep_{lca}*2\)
也就是说我们对于树上的某点\(i\),需要统计它的子树中满足上两个式子中某一个的点的个数
这个可以用权值线段树的合并维护
同时考虑到该路径不会对\(lca\)以上部分的答案产生影响,故考虑树上差分;具体的,在\(s\)和\(t\)处放上\(+1\)标记,而在\(fa_{lca}\)放上两个\(-1\)标记
代码注意事项
1)第二个式子中可能会出现负数,因此将值域整体向右平移\(n\)个单位
2)当这条路径的\(lca\)是一个可以被统计到答案的点时,它会被统计两次(带回发现它对两个式子均成立)。这可以在打\(tag\)时直接判断
代码如下(常数很大,luogu不吸氧\(5317ms\))
#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define lowbit(x) (x)&(-x)
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,a,b) for (int i=a;i>=b;i--)
#define maxd 1000000007
typedef long long ll;
const int N=100000;
const double pi=acos(-1.0);
struct edgenode{
int to,nxt;
}sq[600200];
int n,m,all=0,head[300300],w[300300],dep[300300],fa[300300][20],ans[300300];
int read()
{
int x=0,f=1;char ch=getchar();
while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
return x*f;
}
struct segment_tree{
struct node{
int cnt,lson,rson;
}seg[10000800];
int tot,root[300100];
void insert(int &id,int l,int r,int pos,int val)
{
if (!id) id=(++tot);seg[id].cnt+=val;
if (l==r) return;
int mid=(l+r)>>1;
if (pos<=mid) insert(seg[id].lson,l,mid,pos,val);
else insert(seg[id].rson,mid+1,r,pos,val);
}
int merge(int x,int y,int l,int r)
{
if ((!x) || (!y)) return x+y;
seg[x].cnt+=seg[y].cnt;
int mid=(l+r)>>1;
seg[x].lson=merge(seg[x].lson,seg[y].lson,l,mid);
seg[x].rson=merge(seg[x].rson,seg[y].rson,mid+1,r);
return x;
}
int query(int id,int l,int r,int pos)
{
if (l==r) return seg[id].cnt;
int mid=(l+r)>>1;
if (pos<=mid) return query(seg[id].lson,l,mid,pos);
else return query(seg[id].rson,mid+1,r,pos);
}
}seg1,seg2;
void add(int u,int v)
{
all++;sq[all].to=v;sq[all].nxt=head[u];head[u]=all;
}
int query_lca(int u,int v)
{
if (dep[u]<dep[v]) swap(u,v);
int tmp=dep[u]-dep[v];
rep(i,0,19)
if ((tmp>>i)&1) u=fa[u][i];
if (u==v) return u;
per(i,19,0)
if (fa[u][i]!=fa[v][i]) {u=fa[u][i];v=fa[v][i];}
return fa[u][0];
}
void dfs1(int u,int fu)
{
dep[u]=dep[fu]+1;fa[u][0]=fu;
rep(i,1,19) fa[u][i]=fa[fa[u][i-1]][i-1];
int i;
for (i=head[u];i;i=sq[i].nxt)
{
int v=sq[i].to;
if (v==fu) continue;
dfs1(v,u);
}
}
void dfs2(int u,int fu)
{
int i;
for (i=head[u];i;i=sq[i].nxt)
{
int v=sq[i].to;
if (v==fu) continue;
dfs2(v,u);
seg1.root[u]=seg1.merge(seg1.root[u],seg1.root[v],0,n);
seg2.root[u]=seg2.merge(seg2.root[u],seg2.root[v],0,n*2);
}
if ((w[u]+dep[u]>=0) && (w[u]+dep[u]<=n))
ans[u]+=seg1.query(seg1.root[u],0,n,w[u]+dep[u]);
if ((w[u]-dep[u]>=-n) && (w[u]-dep[u]<=n))
ans[u]+=seg2.query(seg2.root[u],0,2*n,w[u]-dep[u]+n);
}
int main()
{
n=read();m=read();
seg1.tot=0;seg2.tot=0;
rep(i,1,n-1)
{
int u=read(),v=read();
add(u,v);add(v,u);
}
dfs1(1,0);
rep(i,1,n) w[i]=read();
rep(i,1,m)
{
int s=read(),t=read(),lca=query_lca(s,t),fal=fa[lca][0];
seg1.insert(seg1.root[s],0,n,dep[s],1);
seg1.insert(seg1.root[fal],0,n,dep[s],-1);
seg2.insert(seg2.root[t],0,n*2,dep[s]-2*dep[lca]+n,1);
seg2.insert(seg2.root[fal],0,n*2,dep[s]-2*dep[lca]+n,-1);
if (w[lca]+dep[lca]==dep[s]) ans[lca]--;
}
dfs2(1,0);
rep(i,1,n) printf("%d ",ans[i]);
return 0;
}
[luogu1600]NOIp2016D1T2 天天爱跑步的更多相关文章
- [luogu1600 noip2016] 天天爱跑步 (树上差分)
题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵 ...
- luogu1600 [NOIp2016]天天爱跑步 (tarjanLca+dfs)
经过部分分的提示,我们可以把一条路径切成s到lca 和lca到t的链 这样就分为向上的链和向下的链,我们分开考虑: 向上:如果某一个链i可以对点x产生贡献,那么有deep[x]+w[x]=deep[S ...
- 【神仙题】【P1600】【NOIP2016D1T2】天天爱跑步
传送门 Description 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游 ...
- UOJ261 【NOIP2016】天天爱跑步
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000作者博客:http://www.cnblogs.com/ljh2000-jump/转 ...
- BZOJ4719 [Noip2016]天天爱跑步
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000作者博客:http://www.cnblogs.com/ljh2000-jump/转 ...
- noip2016天天爱跑步
题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含 个结点 ...
- bzoj 4719: [Noip2016]天天爱跑步
Description 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.?天天爱跑步?是一个养成类游戏,需要 玩家每天按时上线,完成打卡任务.这个游戏的地图可以看作一一 ...
- [NOIP]2016天天爱跑步
[NOIP]2016天天爱跑步 标签: LCA 树上差分 NOIP Description 小C同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是 ...
- NOIP2016 天天爱跑步 80分暴力
https://www.luogu.org/problem/show?pid=1600 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养 ...
随机推荐
- C#--深入理解类型
今日无事,回顾了一下C#基础知识,颇有收获,就自己的理解,写了这篇文章,如有不对,欢迎指正. C#中的类型可以分为两类:值类型与引用类型,如下图所示. 值类型通常被分配到线程的堆栈上,而引用类型则被分 ...
- asp.net mvc前台显示带htm标签的解决办法(Razor —@Html.Raw())
数据是从后台富文本编辑后丢在数据库后取出的,不加Html.Raw(),前台就会把Html标签一同显示
- python3爬取网页图片路径并写入文件
import reimport urllib.request # 获取网页文件def getHtml(url): response = urllib.request.urlopen('https:// ...
- ThreadPoolExecutor中的submit()方法详细讲解
https://blog.csdn.net/qq_33689414/article/details/72955253
- 26 , CSS 构造表单
1. 表单标签使用 2. 下拉菜单背景 3. 滚动条的使用 4. 结构化表单布局 1 1 1 1. . . . 表单标签的使用 <label for=”name”>姓名: <inpu ...
- 移动开发基础-学习笔记二-字体图标、less、bootstrap入门
1.字体图标 1.字体图标都是用svg图片 1.svg图片不失真 2.svg图标由设计师提供 3.为了减少网络请求,会把svg图标转换成字体图标,放到字体文件中,通过字体库的方式使用 2.制作步骤 1 ...
- arcgis api 3.x for js 入门开发系列十四最近设施点路径分析(附源码下载)
前言 关于本篇功能实现用到的 api 涉及类看不懂的,请参照 esri 官网的 arcgis api 3.x for js:esri 官网 api,里面详细的介绍 arcgis api 3.x 各个类 ...
- Invalid Host header
这个主要是自己遇到很多次了,每次都去网上查改哪里,这次记到自己这里吧,以后把遇到的vue工具的一些问题都整理到这里 在vue中开发的项目有时候需要到手机上看效果,但是你配好本地端口之后,会出现访问内容 ...
- @Resource 与 @Service注解的区别
pring中什么时候用@Resource,什么时候用@service当你需要定义某个类为一个bean,则在这个类的类名前一行使用@Service("XXX"),就相当于讲这个类定义 ...
- SSIS中xml的输入输出
输出为XML的两种方法 1.用数据流, 将平面文件作为DES输出 在SQL里将要输出的数据查询成为单列的字符串: SELECT (SELECT * FROM A FOR XML ROOT('A'),E ...