【uoj261】 NOIP2016—天天爱跑步
http://uoj.ac/problem/261 (题目链接)
题意
给出一棵树,给出一些起点和终点,没走一条路径耗费时间1,每个节点上有一个权值w,问有多少条路径经过这个节点时所用的时间恰好是w。
Solution
转自:http://blog.csdn.net/haarmony/article/details/53259338
约定第${i}$个人起终点的${lca(s[i],t[i])}$为${lca[i]}$,点${i}$深度为${deep[i]}$
- 考虑可能对点${u}$有贡献的第${i}$个跑步者,${lca[i]}$肯定在${u}$或者${u}$上方,否则不经过${u}$点,即${lca[i]==u || lca[i]∉subtree[u]}$
- 基于此前提,只有两种情况(有重合部分):${s[i]∈subtree[u]}$,${t[i]∈subtree[u]}$
分类讨论能被${u}$看见的点${v}$的情况
1.${v}$为${i}$人起点${s[i]}$:${deep[s[i]]-w[u]==dep[u]}$
2.${v}$为${i}$人终点${t[i]}$:${deep[s[i]]+deep[t[i]]-2*deep[lca[i]]-(deep[t[i]]-deep[u])==w[u]}$,即${deep[s[i]]-2*deep[lca[i]]==w[u]-deep[u]}$
我们发现如果做个变换,即${deep[u]+w[u]==deep[s[i]]}$
与${w[u]-deep[u]==deep[s[i]]-2*deep[lca[i]]}$的话,式子右边是不变的,那是不是可以开一个${cnt}$数组来统计一下子树中等于右式的出发点和结束点的个数呢?
- 考虑使用${cnt[2][600600]}$存储起点/终点的${deep[s[i]]}$/${deep[s[i]]-2*deep[lca[i]]}$等于${j}$的点的数量,保存在数组${cnt[0
or 1][j]}$中,那么当dfs中当前点到${u}$的时候,${ans[u]}$就可以从cnt数组中得到,特殊情况是:${u}$为第${i}$人的lca时可能导致${s[i]}$与${t[i]}$各对${ans[u]}$贡献一次,所以式子如下 - ${ans[u]=cnt[0][dep[u]+w[u]]+cnt[0][w[u]-dep[u]]-重复部分}$。两下标为上面两个等式的左式,需要注意的是${dep[s[i]]-2*dep[lca[i]]}$有可能是负数,下标可以统一+300000处理成正数(否则挂成50分)。其中重复部分可以在消除lca标记的时候判掉。
- 维护cnt数组的方式容易想到就是在${s[i],t[i],lca[i]}$上打标记,若当前点为${s[i]}$就计入${cnt[0][dep[s[i]]]}$,${t[i]}$同理,${lca[i]}$就把两个标记再退出去
- 然而如果对于每颗子树建立一个cnt,再把cnt合并的话,复杂度是n^2的。于是发现搜到以v为根的子树时后,对v贡献的只有两个下标,那么在搜索点v前,传两个参数a,b记录一下原来的cnt两个值,dfs(v,a,b)时${ans[v]-=a+b}$即可
细节
细节很多,想清楚再写。
代码
// uoj261
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#define inf 2147483640
#define LL long long
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
using namespace std; const int T=300000,maxn=300010;
struct edge {int to,next;}e[maxn<<1],g[maxn],ct[maxn];
int cnt[2][10000010],h[maxn],hh[maxn],head[maxn];
int deep[maxn],fa[maxn][30],bin[30],Lca[maxn];
int n,m,c1,c2,c3,ans[maxn],cs[maxn],w[maxn],s[maxn],t[maxn]; void linke(int u,int v) {
e[++c1].to=v;e[c1].next=head[u];head[u]=c1;
e[++c1].to=u;e[c1].next=head[v];head[v]=c1;
}
void linkg(int u,int v) {g[++c2].to=v;g[c2].next=h[u];h[u]=c2;}
void linkt(int u,int v) {ct[++c3].to=v;ct[c3].next=hh[u];hh[u]=c3;} void dfs(int x) {
for (int i=1;i<=20;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa[x][0]) {
fa[e[i].to][0]=x;
deep[e[i].to]=deep[x]+1;
dfs(e[i].to);
}
}
int lca(int x,int y) {
if (deep[x]<deep[y]) swap(x,y);
int t=deep[x]-deep[y];
for (int i=0;bin[i]<=t;i++) if (bin[i]&t) x=fa[x][i];
for (int i=20;i>=0;i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return x==y ? x : fa[x][0];
}
void dfs(int x,int ta,int tb) {
for (int j,i=head[x];i;i=e[i].next) if (e[i].to!=fa[x][0]) {
j=e[i].to;
dfs(j,cnt[0][w[j]+deep[j]+T],cnt[1][w[j]-deep[j]+T]);
}
cnt[0][deep[x]+T]+=cs[x];
for (int j,i=hh[x];i;i=ct[i].next) {
j=ct[i].to;
cnt[1][deep[s[j]]-deep[Lca[j]]*2+T]++;
}
ans[x]+=cnt[0][w[x]+deep[x]+T]+cnt[1][w[x]-deep[x]+T]-ta-tb;
for (int j,i=h[x];i;i=g[i].next) {
j=g[i].to;
if (w[x]+deep[x]==deep[s[j]]) ans[x]--;
cnt[0][deep[s[j]]+T]--;cnt[1][deep[s[i]]-2*deep[x]+T]--;
}
}
int main() {
bin[0]=1;for (int i=1;i<=20;i++) bin[i]=bin[i-1]<<1;
scanf("%d%d",&n,&m);
for (int u,v,i=1;i<n;i++) {
scanf("%d%d",&u,&v);
linke(u,v);
}
dfs(1);
for (int i=1;i<=n;i++) scanf("%d",&w[i]);
for (int i=1;i<=m;i++) {
scanf("%d%d",&s[i],&t[i]);
Lca[i]=lca(s[i],t[i]);cs[s[i]]++;
linkg(Lca[i],i);
linkt(t[i],i);
}
dfs(1,0,0);
for (int i=1;i<=n;i++) printf("%d ",ans[i]);
return 0;
}
Solution
树链剖分维护启发式合并来一发。
代码
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#define LL long long
#define inf (1ll<<30)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
using namespace std; const int maxn=300010,rem=300000;
int cnt,n,m,ans[maxn],W[maxn];
int son[maxn],fa[maxn][30],bin[maxn],head[maxn],size[maxn],deep[maxn];
int c[maxn<<2];
struct edge {int to,next;}e[maxn<<1];
vector<pair<int,int> >v[maxn]; void link(int u,int v) {
e[++cnt]=(edge){v,head[u]};head[u]=cnt;
e[++cnt]=(edge){u,head[v]};head[v]=cnt;
}
void dfs(int x) {
size[x]=1;
for (int i=1;i<=20;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa[x][0]) {
fa[e[i].to][0]=x;
deep[e[i].to]=deep[x]+1;
dfs(e[i].to);
size[x]+=size[e[i].to];
if (size[son[x]]<size[e[i].to]) son[x]=e[i].to;
}
}
int lca(int x,int y) {
if (deep[x]<deep[y]) swap(x,y);
int t=deep[x]-deep[y];
for (int i=0;bin[i]<=t;i++) if (t&bin[i]) x=fa[x][i];
for (int i=20;i>=0;i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return x==y ? x : fa[x][0];
}
void modify(int x,int val) {
for (int j=v[x].size(),i=0;i<j;i++)
c[v[x][i].first+rem]+=v[x][i].second*val;
for (int i=head[x];i;i=e[i].next)
if (e[i].to!=fa[x][0]) modify(e[i].to,val);
}
void dfs(int x,int tp) {
for (int i=head[x];i;i=e[i].next)
if (e[i].to!=son[x] && e[i].to!=fa[x][0]) dfs(e[i].to,0);
if (son[x]) dfs(son[x],1);
for (int i=head[x];i;i=e[i].next)
if (e[i].to!=son[x] && e[i].to!=fa[x][0]) modify(e[i].to,1);
ans[x]+=c[deep[x]+W[x]+rem];
for (int j=v[x].size(),i=0;i<j;i++) c[v[x][i].first+rem]+=v[x][i].second;
ans[x]+=c[deep[x]-W[x]+rem];
if (!tp) modify(x,-1);
} int main() {
bin[0]=1;for (int i=1;i<=20;i++) bin[i]=bin[i-1]<<1;
scanf("%d%d",&n,&m);
for (int x,y,i=1;i<n;i++) {
scanf("%d%d",&x,&y);
link(x,y);
}
dfs(1);
for (int i=1;i<=n;i++) scanf("%d",&W[i]);
for (int a,b,i=1;i<=m;i++) {
scanf("%d%d",&a,&b);int f=lca(a,b);
if (a==b && W[a]==0) ans[a]++; //pay attention
v[a].push_back(pair<int,int>(deep[a],1));
v[b].push_back(pair<int,int>(2*deep[f]-deep[a],1));
v[f].push_back(pair<int,int>(deep[a],-1));
v[f].push_back(pair<int,int>(2*deep[f]-deep[a],-1));
}
dfs(1,0);
for (int i=1;i<=n;i++) printf("%d ",ans[i]);
return 0;
}
【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 ...
随机推荐
- PAT 1021. 个位数统计 (15)
给定一个k位整数N = dk-1*10k-1 + ... + d1*101 + d0 (0<=di<=9, i=0,...,k-1, dk-1>0),请编写程序统计每种不同的个位数字 ...
- P3398 仓鼠找sugar
P3398 仓鼠找sugar 题目描述 小仓鼠的和他的基(mei)友(zi)sugar住在地下洞穴中,每个节点的编号为1~n.地下洞穴是一个树形结构.这一天小仓鼠打算从从他的卧室(a)到餐厅(b),而 ...
- 安装 SQL SERVER 2008 必须使用 "角色管理工具" 错误 的 解决方案 (转)
刚在服务器(Win2008)上安装SqlServer2008的时候出现了这么一个报错——必须使用“角色管理工具”安装或配置Microsoft .NET Framework 3.5 SP1.一开始以为是 ...
- 转 FileStream Read File
FileStream Read File [C#] This example shows how to safely read file using FileStream in C#. To be s ...
- cpu负载和利用率
理解Linux系统负荷 linux里的CPU负载
- HP DL60 Gen9 安装CentOS 6.5
由于Gen 9的VID中自带RETHAT 5.X-7.0的驱动, 所以不需要制作驱动盘. 一, 准备工作 1. 制作 CentOS安装盘 2. 配置阵列, 开机过了自检后按F10. 自己看着配... ...
- 工作随笔——Intellij_idea-14官方快捷键中文版
听说Intellij Idea好几年了.因为快捷键的原因,所以一直没有放弃eclipse.上周末抽了点时间,用google翻译+自己实践翻译了一下官方的快捷键. 基本做完的时候在百度文库上突然搜索到一 ...
- 闲扯 『 document.write 』
初春的晚上,闲来无事,聊聊 document.write 方法. document.write 使用方式非常简单,把 "字符串化"(不好意思,这可能是我自己创造的名词)的 html ...
- 快速开发之代码生成器(asp.net mvc4 + easyui + knockoutjs)
一.前言 作为一个码农这么多年,一直在想怎么提高我们的编码效率,关于如何提高编码效率,我自己的几点体会 1.清晰的项目结构,要编写代码的地方集中 2.实现相同功能的代码量少并且清晰易懂 3.重复或有规 ...
- 基于FPGA的通信系统实验
伪随机信号发生器 1.伪随机信号发生器原理 伪随机信号发生器又叫PN序列发生器或者是m序列发生器.m序列是一种线性反馈寄存器序列,m序列的产生可以利用r级寄存器产生长度为2^r-1的m序列,该实验中采 ...