NOIP2016 天天爱跑步
4719: [Noip2016]天天爱跑步
Time Limit: 40 Sec Memory Limit: 512 MBSubmit: 1457 Solved: 488
[Submit][Status][Discuss]
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
题目分析:
先来分析一条链的做法,在链上路径必然只有两种情况,要么从左往右要么从右往左
即: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 }