UOJ261 【NOIP2016】天天爱跑步
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!
Description
Input
Output
输出1行N 个整数,第个整数表示结点的观察员可以观察到多少人。
Sample Input
2 3
1 2
1 4
4 5
4 6
0 2 5 1 2 3
1 5
1 3
2 6
Sample Output
HINT
25分
考虑此时$n$很小,可以对于每条路径上暴力模拟,经过某个点时可以看一下当前时刻,是否跟经过的点的$w$相等,如果相等,则贡献加一。
45分
注意到测试点$9-12$时,保证$m$条路径的出发点都是$1$,那么我们可以考虑如果将$1$作为树根,那么一条路径怎样才能对于它经过的点产生贡献。
不难看出对于一个点$i$,只有在$deep[i]=w[i]$,才有可能有贡献。
我在考场上是直接用的链剖$+$线段树,因为这就变成模板题了,而且$n$不到$10w$,尽管复杂度偏高,但是不易错。
直接对于每条路径经过的点在线段树上增加$1$次经过次数,显然只有$deep$与$w$相等的点才会产生贡献。
事实上对于$S=1$的情况有线性的算法,正解会详细介绍,不再赘述。
60分
注意到测试点$6-8$时,题目保证树退化成链。我们观察一下对于链而言,有什么特别的地方。首先要明确,此时m条路径在链上肯定是要么往左要么往右,即$S<=T$或者$S>T$。
先只考虑$S<=T$的情况,如果对于$S$到$T$之间的点i,要产生贡献的话,肯定满足$i-S=w[i]$,移项可得$S=i-w[i]$时才可以满足要求。
注意到等式右边只与$i$本身有关,不妨设为$K[i]$,所以题目变成了查询$S$到$T$之间$K[i]$等于$S$的$i$的数量。
因为题目只涉及到首和尾,我们可以很容易联想到差分,即对于$S$打上$+1$标记,$T$打上$-1$标记。
根据上述思路,我们考虑具体做法:对于每个点$i$,我们很容易发现只有从一个特定的点出发才有可能对$i$产生贡献。
我们考虑维护一个统计数组$A$,$A[k]$表示的是处理到当前的结点时,从$k$出发的路径(而且还没有走到终点)有多少条。
这样对于每个点$i$,我们只要查询一下所对应的$A[K[i]]$就可以了,根据上面的分析,这就是我们的答案了。
有一点注意处理:处理一个点$i$时,我们需要把以$i$为起点的路径加入统计数组$A$,再计算这个结点的贡献,最后再把以这个结点为终点的路径从$A$中消除,具体可以用$vector$实现(上述处理顺序的必要性仔细想想就很容易想通了)。
而对于$S>T$的情况完全类似,只是需要把$K[i]$定义为$i+w[i]$,其余做法完全类似。
100分
题目中设计的几个档次的部分分其实暗示已经很明显了。
链的做法离正解就不远了。
而$S=1$和$T=1$是在告诉我们什么呢?
拆路径!
很容易发现,一条$S$到$T$的路径可以拆成一条$S$到$LCA$的路径和$LCA$到$T$的路径,然后对于这两条路径,一条往上,一条往下,都可以对应成链的处理方式了!
考虑对于每条路径,先将其拆分成两条路径(为了简化对$LCA$在两条路径中都出现的各种情况,我们可以先就让$LCA$出现两次,如果最后发现$LCA$是有贡献的,只需$-1$即可),同样,我们先只考虑向上的路径。
如果我们对于$S$在统计数组$A$上打上$1$的标记,$LCA$在统计数组$A$上打上$-1$的标记,那么题目转化为求一个点的子树和。
考虑上述做法正确性:因为只有$S$到$LCA$路径之间的点会产生贡献,而当这个点位于路径之间时,子树和会产生$1$的贡献,而在$S$的子树中或者$LCA$的上方都不会产生贡献。
具体实现呢?
对于一个点$i$,产生贡献的条件是$deep[S]-deep[i]=w[i]$,同样令$K[i]=deep[i]+w[i]$,当我们$dfs$到$i$时查询$A[k[i]]$的值即为贡献。
为了保证正确性,我们思考统计答案的方式和顺序。
首先我们肯定是在处理完$i$的子树之后再来处理$i$(想想就知道了),然后我们需要再把以$i$出发的向上的路径加入统计数组,再进行查询,最后把以$i$为终点的路径所产生的贡献在统计数组$A$中消除即可。
注意到我们上面维护的仅仅是一个点的深度,由于同一深度的点很多,所以我们查询的时候会发现会把不在同一子树的点统计入答案,那怎么办呢?我们考虑对于一个点要查询子树和,肯定是只要单独地考虑这一个子树的贡献,所以我们可以记录进入$i$时$A[k[i]]$的值,再在访问完$i$的子树之后统计答案时,看一下此时新的$A[k[i]]$的值。
容易发现新的值减掉进入时的,才是真正的$i$的子树中的$A[k[i]]$的值。
这样我们就可以避免把别的子树的答案统计进来了。
对于向下的点做法类似,有一点复杂的地方就是等式变成了$deep[T]-deep[i]=len-w[i]$($len$为路径长度),发现如果这样做的话会出现负数,那么我们就把统计数组向右平移$3*10^5$位就可以了。
上述做法如果采用的是倍增求$LCA$的话,复杂度就是$O(nlogn)$;
如果用$tarjan$离线求$LCA$的话,可以做到$O(n+m)$。
注意事项
对于树上每个结点,统计答案时不能直接查询在统计数组中的对应的路径条数,
而应该统计$dfs$进入$i$时,和访问完$i$的子树时的变化量。
//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
using namespace std;
typedef long long LL;
const int MAXN = ;
const int MAXM = ;
int n,m,ecnt,first[MAXN],next[MAXM],to[MAXM],f[MAXN][],deep[MAXN],ans[MAXN],val[MAXN],tong[MAXN],MAXD,w[MAXN],num[];
vector<int>ljh[MAXN],ljh2[MAXN],ljh3[MAXN];
struct node{ int s,t,lca,len;}a[MAXN];
inline int getint(){
int w=,q=; char c=getchar(); while((c<''||c>'') && c!='-') c=getchar();
if(c=='-') q=,c=getchar(); while (c>=''&&c<='') w=w*+c-'',c=getchar(); return q?-w:w;
}
inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; }
inline void init(int x,int fa){ for(int i=first[x];i;i=next[i]) { int v=to[i]; if(v==fa) continue; deep[v]=deep[x]+; init(v,x); f[v][]=x; } }
inline int lca(int x,int y){
if(deep[x]<deep[y]) swap(x,y); int t=; while((<<t)<=deep[x]) t++; t--;
for(int i=t;i>=;i--) if(deep[x]-(<<i)>=deep[y]) x=f[x][i]; if(x==y) return y;
for(int i=t;i>=;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][];
} inline void dfs(int x,int fa){
int now=w[x]+deep[x],cun; if(now<=MAXD) cun=tong[now];
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue;
dfs(v,x);
}
tong[deep[x]]+=val[x]; if(now<=MAXD) ans[x]=tong[now]-cun;
for(int i=,ss=ljh[x].size();i<ss;i++) tong[deep[ljh[x][i]]]--;
} inline void DFS(int x,int fa){
int now=deep[x]-w[x],cun; now+=; cun=num[now];
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue;
DFS(v,x);
}
for(int i=,ss=ljh2[x].size();i<ss;i++) num[+ljh2[x][i]]++;
ans[x]+=num[now]-cun;
for(int i=,ss=ljh3[x].size();i<ss;i++) num[+ljh3[x][i]]--;
} inline void work(){
n=getint(); m=getint(); int x,y; for(int i=;i<n;i++) { x=getint(); y=getint(); link(x,y); link(y,x); }
for(int i=;i<=n;i++) w[i]=getint(); deep[]=; init(,); for(int i=;i<=n;i++) MAXD=max(MAXD,deep[i]);
for(int j=;j<=;j++) for(int i=;i<=n;i++) f[i][j]=f[f[i][j-]][j-];
for(int i=;i<=m;i++) {
a[i].s=getint(),a[i].t=getint(),val[a[i].s]++;
a[i].lca=lca(a[i].s,a[i].t),a[i].len=deep[a[i].s]+deep[a[i].t]-deep[a[i].lca]*;
ljh[a[i].lca].push_back(a[i].s);
}
dfs(,);
for(int i=;i<=m;i++) {
ljh2[a[i].t].push_back(deep[a[i].t]-a[i].len);
ljh3[a[i].lca].push_back(deep[a[i].t]-a[i].len);
}
DFS(,);
for(int i=;i<=m;i++) if(deep[a[i].s]-deep[a[i].lca]==w[a[i].lca]) ans[a[i].lca]--;
for(int i=;i<=n;i++) printf("%d ",ans[i]);
} int main()
{
work();
return ;
}
//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
using namespace std;
typedef long long LL;
const int MAXN = ;
const int MAXM = ;
int n,m,ecnt,first[MAXN],next[MAXM],to[MAXM],f[MAXN][],deep[MAXN],ans[MAXN],val[MAXN],tong[MAXN],MAXD,w[MAXN],num[];
int head[MAXN],tt[MAXM],nn[MAXM],father[MAXN],vis[MAXN];
vector<int>ljh[MAXN],ljh2[MAXN],ljh3[MAXN];
struct node{ int s,t,lca,len;}a[MAXN];
inline int getint(){
int w=,q=; char c=getchar(); while((c<''||c>'') && c!='-') c=getchar();
if(c=='-') q=,c=getchar(); while (c>=''&&c<='') w=w*+c-'',c=getchar(); return q?-w:w;
}
inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; }
inline void LINK(int x,int y){ nn[++ecnt]=head[x]; head[x]=ecnt; tt[ecnt]=y; }
inline int find(int x){ if(father[x]!=x) father[x]=find(father[x]); return father[x]; }
inline void init(int x,int fa){
father[x]=x; vis[x]=;
for(int i=head[x];i;i=nn[i]) {
int v=tt[i];
if(x==a[v].s&&vis[a[v].t]) a[v].lca=find(a[v].t);
if(x==a[v].t&&vis[a[v].s]) a[v].lca=find(a[v].s);
}
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue;
deep[v]=deep[x]+; init(v,x); father[v]=x;
f[v][]=x;
}
} inline int lca(int x,int y){
if(deep[x]<deep[y]) swap(x,y); int t=; while((<<t)<=deep[x]) t++; t--;
for(int i=t;i>=;i--) if(deep[x]-(<<i)>=deep[y]) x=f[x][i]; if(x==y) return y;
for(int i=t;i>=;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][];
} inline void dfs(int x,int fa){
int now=w[x]+deep[x],cun; if(now<=MAXD) cun=tong[now];
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue;
dfs(v,x);
}
tong[deep[x]]+=val[x]; if(now<=MAXD) ans[x]=tong[now]-cun;
for(int i=,ss=ljh[x].size();i<ss;i++) tong[deep[ljh[x][i]]]--;
} inline void DFS(int x,int fa){
int now=deep[x]-w[x],cun; now+=; cun=num[now];
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue;
DFS(v,x);
}
for(int i=,ss=ljh2[x].size();i<ss;i++) num[+ljh2[x][i]]++;
ans[x]+=num[now]-cun;
for(int i=,ss=ljh3[x].size();i<ss;i++) num[+ljh3[x][i]]--;
} inline void work(){
n=getint(); m=getint(); int x,y; for(int i=;i<n;i++) { x=getint(); y=getint(); link(x,y); link(y,x); }
for(int i=;i<=n;i++) w[i]=getint(); ecnt=;
for(int i=;i<=m;i++) { a[i].s=getint(),a[i].t=getint(),val[a[i].s]++; LINK(a[i].s,i); LINK(a[i].t,i);}
deep[]=; init(,); for(int i=;i<=n;i++) MAXD=max(MAXD,deep[i]);
for(int j=;j<=;j++) for(int i=;i<=n;i++) f[i][j]=f[f[i][j-]][j-];
for(int i=;i<=m;i++) {
a[i].len=deep[a[i].s]+deep[a[i].t]-deep[a[i].lca]*;
ljh[a[i].lca].push_back(a[i].s);
}
dfs(,);
for(int i=;i<=m;i++) {
ljh2[a[i].t].push_back(deep[a[i].t]-a[i].len);
ljh3[a[i].lca].push_back(deep[a[i].t]-a[i].len);
}
DFS(,);
for(int i=;i<=m;i++) if(deep[a[i].s]-deep[a[i].lca]==w[a[i].lca]) ans[a[i].lca]--;
for(int i=;i<=n;i++) printf("%d ",ans[i]);
} int main()
{
work();
return ;
}
UOJ261 【NOIP2016】天天爱跑步的更多相关文章
- [NOIp2016]天天爱跑步 线段树合并
[NOIp2016]天天爱跑步 LG传送门 作为一道被毒瘤出题人们玩坏了的NOIp经典题,我们先不看毒瘤的"动态爱跑步"和"天天爱仙人掌",回归一下本来的味道. ...
- [Noip2016]天天爱跑步 LCA+DFS
[Noip2016]天天爱跑步 Description 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.?天天爱跑步?是一个养成类游戏,需要玩家每天按时上线,完成打卡任 ...
- 【LG1600】[NOIP2016]天天爱跑步
[LG1600][NOIP2016]天天爱跑步 题面 洛谷 题解 考虑一条路径\(S\rightarrow T\)是如何给一个观测点\(x\)造成贡献的, 一种是从\(x\)的子树内出来,另外一种是从 ...
- NOIP2016天天爱跑步 题解报告【lca+树上统计(桶)】
题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含 nn个 ...
- BZOJ4719 [Noip2016]天天爱跑步
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000作者博客:http://www.cnblogs.com/ljh2000-jump/转 ...
- noip2016天天爱跑步
题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含 个结点 ...
- bzoj 4719: [Noip2016]天天爱跑步
Description 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.?天天爱跑步?是一个养成类游戏,需要 玩家每天按时上线,完成打卡任务.这个游戏的地图可以看作一一 ...
- NOIP2016 天天爱跑步 80分暴力
https://www.luogu.org/problem/show?pid=1600 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养 ...
- 4719: [Noip2016]天天爱跑步
Time Limit: 40 Sec Memory Limit: 512 MB Submit: 1986 Solved: 752 [Submit][Status][Discuss] Descripti ...
- NOIP2016 天天爱跑步 线段树合并_桶_思维题
竟然独自想出来了,好开心 Code: #include<bits/stdc++.h> #define setIO(s) freopen(s".in","r&q ...
随机推荐
- 02Spring_Ioc和DI介绍
什么是IOC? IoC: 控制反转, 解决程序对象紧密耦合问题(工厂+反射+ 配置文件), 将程序中原来构造对象的权限,交给IoC容器来构造,当程序需要对象,找IoC容器获取.
- 兼容利器之X-UA-Compatible
文档兼容模式 不同浏览器之间经常产生各种奇异的现象,为了解决这些问题,使用以下方法,发现很多问题自动消失不见了 <meta http-equiv="X-UA-Compatible&qu ...
- KeyBord事件从Activtiy层往下分发详细过程代码示例
step1:调用Activity成员函数dispatchKeyEvent public boolean dispatchKeyEvent(KeyEvent event) { // Let action ...
- android:ToolBar详解(手把手教程)(转)
来源 http://blog.mosil.biz/2014/10/android-toolbar/ 编辑推荐:稀土掘金,这是一个针对技术开发者的一个应用,你可以在掘金上获取最新最优质的技术干货,不仅仅 ...
- codevs http://www.codevs.cn/problem/?problemset_id=1 循环、递归、stl复习题
12.10高一练习题 1.要求: 这周回顾复习的内容是循环.递归.stl. 不要因为题目简单就放弃不做,现在就是练习基础. 2.练习题: (1)循环 题目解析与代码见随笔分类 NOI题库 htt ...
- nginx学习(1):编译、安装、启动
一.下载 从官网http://nginx.org/en/download.html 下载稳定版(目前最新稳定版是1.6.2) 二.解压 tar zxf nginx-1.6.2.tar.gzcd ngi ...
- jQuery学习笔记(三):选择器总结
这一节详细的总结jQuery选择器. 一.基础选择器 $('#info'); // 选择id为info的元素,id为document中是唯一的,因此可以通过该选择器获取唯一的指定元素$('.infoC ...
- java并发:简单面试问题集锦
多线程:Simultaneous Multithreading,简称SMT. 并行.并发 并行性(parallelism)指两个或两个以上的事件在同一时刻发生,在多道程序环境下,并行性使多个程序同一时 ...
- 与TCP/IP协议的初次见面(一)
引言 最近LZ有了一点时间,于是便拿出TCP/IP的书本开始啃.开始的时候,啃起来枯燥无味,现在好不容易有点开窍,于是赶忙记录一下,生怕自己一转眼就给忘了.不过计算机系统原理就有点可惜了,最近一直没时 ...
- ActiveMQ_Linux安装(一)
一.下载:apache-activemq-5.14.0-bin.tar.gz http://activemq.apache.org/activemq-5140-release.html 二.安 ...