NOIP2016 天天爱跑步

4719: [Noip2016]天天爱跑步

Time Limit: 40 Sec  Memory Limit: 512 MB
Submit: 1457  Solved: 488
[Submit][Status][Discuss]

Description

小c同学认为跑步非常有趣,于是决定制作一款叫做《天天爱跑步》的游戏。?天天爱跑步?是一个养成类游戏,需要
玩家每天按时上线,完成打卡任务。这个游戏的地图可以看作一一棵包含 N个结点和N-1 条边的树, 每条边连接两
个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从1到N的连续正整数。现在有个玩家,第个玩家的
起点为Si ,终点为Ti  。每天打卡任务开始时,所有玩家在第0秒同时从自己的起点出发, 以每秒跑一条边的速度,
不间断地沿着最短路径向着自己的终点跑去, 跑到终点后该玩家就算完成了打卡任务。 (由于地图是一棵树, 所以
每个人的路径是唯一的)小C想知道游戏的活跃度, 所以在每个结点上都放置了一个观察员。 在结点的观察员会选
择在第Wj秒观察玩家, 一个玩家能被这个观察员观察到当且仅当该玩家在第Wj秒也理到达了结点J  。 小C想知道
每个观察员会观察到多少人?注意: 我们认为一个玩家到达自己的终点后该玩家就会结束游戏, 他不能等待一 段时
间后再被观察员观察到。 即对于把结点J作为终点的玩家: 若他在第Wj秒重到达终点,则在结点J的观察员不能观察
到该玩家;若他正好在第Wj秒到达终点,则在结点的观察员可以观察到这个玩家。

Input

第一行有两个整数N和M 。其中N代表树的结点数量, 同时也是观察员的数量, M代表玩家的数量。
接下来n-1 行每行两个整数U和V ,表示结点U 到结点V 有一条边。
接下来一行N 个整数,其中第个整数为Wj , 表示结点出现观察员的时间。
接下来 M行,每行两个整数Si和Ti,表示一个玩家的起点和终点。
对于所有的数据,保证 。
1<=Si,Ti<=N,0<=Wj<=N
 

Output

输出1行N 个整数,第个整数表示结点的观察员可以观察到多少人。

 

Sample Input

6 3
2 3
1 2
1 4
4 5
4 6
0 2 5 1 2 3
1 5
1 3
2 6

Sample Output

2 0 0 1 1 1

HINT

 


对于1号点,W1=0,故只有起点为1号点的玩家才会被观察到,所以玩家1和玩家2被观察到,共2人被观察到。

对于2号点,没有玩家在第2秒时在此结点,共0人被观察到。

对于3号点,没有玩家在第5秒时在此结点,共0人被观察到。

对于4号点,玩家1被观察到,共1人被观察到。

对于5号点,玩家1被观察到,共1人被观察到。

对于6号点,玩家3被观察到,共1人被观察到

 

 

题目分析:

先来分析一条链的做法,在链上路径必然只有两种情况,要么从左往右要么从右往左

即:S <= T || S >T 两种情况

//仅分析从左往右走的情况(对应到树上为从下往上),另一种情况类比

那我们考虑一个点i满足什么条件时可以观察到一个玩家:w[i] = i - S 

对于这个式子,我们可以进行移项,得到 S = i - w[i],i - w[i]对于一个观察者来说是定值

所以其实,对于这种路线而言,会对一个观察者做出贡献的人,其实都来自于同一个起点

也就是只有从一个特定起点出发才可能(如果一个玩家从S出发但整条路径不经过i便不贡献)对i做出贡献

(图片来自menci的博客)

于是,我们可以将问题进行如下转换:

1.在区间l - r上每个点放一个数字(添加一条路径的时候,把起点加进去)

2.查询一个点上,有多少个数字等于特定的S(即每个观察员会观察到几个玩家)

(由于问题在一棵树上,可以树链剖分之后转换为序列)

分析至此,第一种做法产生了:  树链剖分 + 主席树

主席树不会怎么办!来看第二种做法

将问题放到树上来考虑:首先来对一条路径拆分,一条u -> v的路径可以拆分成

u -> lca (从下往上走) 和  lca -> v(从上往下走) 两部分,仍然只讲解从下往上的部分

只考虑从下向上的路径的话,会对一个观察员 i 做出贡献的玩家必须满足如下条件:

①起点在 i 的子树中

②并且路径经过点i

③并且起点是上面分析中的特定的点

(转换到树上之后,这样特定的点不再像序列上一样是一个点了,而是深度相同的一些点)

先不考虑条件二,如果只有条件一三的话,我们只要统计这样一个东西:

一个点的子树中,有多少条起点在特定深度的路径。条数就是观察员能观察到的玩家数量

这个问题只要一遍dfs就可以解决(运用差分的思想)

那条件二呢?可以考虑用全部的路径数量 - 不满足条件二的路径数量

如何计算不满足条件二的路径数量呢?

不难发现,起点在 i 的子树中,并且路径不经过点i 的路径,终点一定也在i的子树中

并且起点终点的lca必定也在i的子树中,那么我们只要记录以每个点为lca的路径是哪些

在dfs点i的子树中的点的时候,把以子树中的点为lca的路径减去即可(一个细节复杂的树形dp)

对于另一种从上往下走的路径,类比以上即可

至此做法二结束 lca + 树上差分 + 脑洞统计

CODES:

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cstring>
  5 #include <vector>
  6 #define max(a, b) ((a) > (b) ? (a) : (b))
  7 #define min(a, b) ((a) < (b) ? (a) : (b))
  8 #define abs(a) ((a) < 0 ? (-1 * (a)) : a)
  9 
 10 inline void read(int &x)
 11 {
 12     x = 0;char ch = getchar(), c = ch;
 13     while(ch < '0' || ch > '9')c = ch, ch = getchar();
 14     while(ch <= '9' && ch >= '0')x = x * 10 + ch - '0', ch = getchar();
 15     if(c == '-')x = -x;
 16 }
 17 
 18 const int INF = 0x3f3f3f3f;
 19 const int MAXN = 300000 + 10;
 20 const int MAXM = 400000 + 10; 
 21 const int PIANYI = 300000;
 22 
 23 struct Edge
 24 {
 25     int u,v,next;
 26     Edge(int _u, int _v, int _next){u = _u;v = _v;next = _next;}
 27     Edge(){}
 28 }edge[MAXN << 1];
 29 int head[MAXN], cnt;
 30 inline void insert(int a, int b)
 31 {
 32     edge[++cnt] = Edge(a,b,head[a]);
 33     head[a] = cnt;
 34 }
 35 
 36 struct qEdge
 37 {
 38     int u,v,next,id;
 39     qEdge(int _u, int _v, int _next, int _id){u = _u;v = _v;next = _next;id = _id;}
 40     qEdge(){}
 41 }qedge[MAXN << 1];
 42 int qhead[MAXN], qcnt = 1;
 43 inline void qinsert(int a, int b, int id)
 44 {
 45     qedge[++qcnt] = qEdge(a,b,qhead[a],id);
 46     qhead[a] = qcnt;
 47 }
 48 
 49 int n,m,tong[2000000],ma;
 50 std::vector<int> vec[MAXN];
 51 std::vector<int> end[MAXN];
 52 
 53 struct Node
 54 {
 55     int s, t, lca, lenth;
 56 }node[MAXM];
 57 
 58 int w[MAXN],fa[MAXN],b[MAXN],deep[MAXN];
 59 
 60 int find(int x)
 61 {
 62     return x == fa[x] ? x : fa[x] = find(fa[x]);
 63 }
 64 
 65 void dfs2(int u)
 66 {
 67     b[u] = 1;
 68     for(register int pos = head[u];pos;pos = edge[pos].next)
 69     {
 70         int v = edge[pos].v;
 71         if(b[v])continue;
 72         deep[v] = deep[u] + 1;
 73         ma = max(ma, deep[v]);
 74         dfs2(v);
 75         int f1 = find(u), f2 = find(v);
 76         fa[f2] = f1;
 77     }
 78     for(register int pos = qhead[u];pos;pos = qedge[pos].next)
 79     {
 80         int v = qedge[pos].v, tmp = find(v);
 81         node[qedge[pos].id].lca = tmp;
 82         node[qedge[pos].id].lenth = deep[u] + deep[v] - 2 * deep[tmp];
 83     }
 84 }
 85 
 86 void tarjan_lca()
 87 {
 88     deep[1] = 0;
 89     dfs2(1);
 90 }
 91 
 92 int ans[MAXN],s[MAXN];
 93 //ans[i]表示i这个点能看到多少人,s[i]表示在i这个点有几个起点,t[i]表示在i这个点有几个终点 
 94 
 95 void dfs1(int u)
 96 {
 97     b[u] = 1;
 98     int tmp1;
 99     if(deep[u] + w[u] <= ma)tmp1 = tong[deep[u] + w[u]];
100     //递归子节点 
101     for(register int pos = head[u];pos;pos = edge[pos].next)
102     {
103         int v = edge[pos].v;
104         if(b[v]) continue;
105         dfs1(v);
106     }
107     tong[deep[u]] += s[u];
108     if(deep[u] + w[u] <= ma) ans[u] += tong[deep[u] + w[u]] - tmp1; 
109     //把以u为LCA的路径删除掉 
110     for(register int i = vec[u].size() - 1;i >= 0;-- i)    
111         -- tong[deep[node[vec[u][i]].s]];
112 }
113 
114 void dfs3(int u)
115 {
116     b[u] = 1;
117     int tmp2;
118     tmp2 = tong[w[u] - deep[u] + PIANYI];
119     //递归子节点 
120     for(register int pos = head[u];pos;pos = edge[pos].next)
121     {
122         int v = edge[pos].v;
123         if(b[v]) continue;
124         dfs3(v);
125     }
126     for(register int i = end[u].size() - 1;i >= 0;-- i)
127         ++ tong[node[end[u][i]].lenth - deep[u] + PIANYI];
128     ans[u] += tong[w[u] - deep[u] + PIANYI] - tmp2; 
129     for(register int i = vec[u].size() - 1;i >= 0;-- i)    
130         -- tong[node[vec[u][i]].lenth - deep[node[vec[u][i]].t] + PIANYI];
131 }
132 
133 int main()
134 {
135     read(n), read(m);
136     for(register int i = 1;i < n;++ i)
137     {
138         int tmp1, tmp2;
139         read(tmp1), read(tmp2);
140         insert(tmp1, tmp2);
141         insert(tmp2, tmp1); 
142     }
143     for(register int i = 1;i <= n;++ i)
144         read(w[i]), fa[i] = i;
145     for(register int i = 1;i <= m;++ i)
146     {
147         read(node[i].s), read(node[i].t);
148         qinsert(node[i].s, node[i].t, i);
149         qinsert(node[i].t, node[i].s, i); 
150         ++ s[node[i].s]; 
151     } 
152     tarjan_lca();
153     for(register int i = 1;i <= n;++ i)
154         vec[node[i].lca].push_back(i), end[node[i].t].push_back(i);
155     memset(b, 0, sizeof(b));
156     dfs1(1);
157     memset(b, 0, sizeof(b));
158     dfs3(1);
159     for(register int i = 1;i <= m;++ i)
160         if(deep[node[i].s] == deep[node[i].lca] + w[node[i].lca])
161             -- ans[node[i].lca];
162     for(register int i = 1;i < n;++ i)
163         printf("%d ", ans[i]);
164     printf("%d", ans[n]);
165     return 0;
166 }
View Code
posted @ 2017-11-01 16:34  illyaillyasviel  阅读(300)  评论(2编辑  收藏  举报