zoukankan      html  css  js  c++  java
  • NOIP2016 天天爱跑步

    题目链接

    传送门

    题目分析

    虽然好像看起来不难,但是就是想不到啊
    部分分感觉可以拿(60pts),但还没打代码验证完,分析暂时不放上来

    2019/11/7 UPD:
    把最后一档特殊点的部分分想了一下,这下应该(80pts)了,口胡一下部分分思路吧,不保证对

    • #1~#2:所有人起点等于自己终点:
      起点所在点(w_i)若为(0)则能观测到,否则不能,其他点不能

    • #3~#4:(w_i = 0)
      每个点上有起点则可被观测到,否则不能

    • #5:对于每条路径暴力模拟即可

    • #6~#8:树退化为一条链,,其中(1)(2)有边,(2)(3)有边,(cdots)(n - 1)(n)有边
      考虑每个观测点(i),它能观测到的点只可能是来自(i - w_i)(i + w_i)两个点的,于是考虑在每个起点挂一个(vector)存储每条路径的长度,然后对于每个(vector)排一下序,对于每个查询直接定位到往前(w_i)个点和往后第(w_i)个点,在(vector)上二分找到第一个路径长度超过(w_i)的,然后更新答案,均摊复杂度(O(nlog2(n)))

    • #9~#12:对于每条路径的终点在树上打一下(+1)标记,类似树上差分,然后查询(w_i = dep_i)的点的子树和即可

    • #13~#16:类似上一档部分分,查询每个观测点(i)满足(dep_i+w_i = dep_st)的子树中节点的起点个数
      考虑在(dfs)序上做,记录一下每个节点的进栈序和出栈序,开桶维护(dep),扫描到每个进栈序的时候在桶中统计一下,对于一个节点的查询,记录它进栈和出栈时桶(dep_i + w_i)中的节点个数,相减得到增量即得到答案

    (Luogu)题解第一页的@一扶苏一大佬写了每档部分分的分析,可能有些不一样,也可以去看他的

    看一看(NOIP2016)年鉴,发现其实正解就是许多档部分分的想法的整合,这启示我们在拿到一道难题的时候,如果对正解暂时没有什么思路,可以考虑尽可能多的想部分分怎么打,一是你反正还是要对拍,二是部分分的做法可能内嵌(std)

    (forall s_i = 1)的部分分入手,发现(forall s_i = 1)时其实就是对于每一条路径头尾差分一下,然后对于每个(dep_i = w_i)的点统计一下它的子树和
    那么正解呢
    考虑把每一条路径拆成上行和下行两条路径,感觉和上面的部分分有共通之处,能不能继续考虑差分呢
    答案是可以的
    先考虑上行,还是在起点打(+1)标记,但是这样还不行,万一在(LCA)的地方拐下去了呢,所以还要在(LCA)的地方减一下
    考虑这样的话一个点要满足什么条件
    设起点为(st),那么有(dep_st - w_i = dep_i)时,从起点出发,跑过这个点的时候可以被观测到
    移项一下 (dep_st = dep_i + w_i),即在(dep_st)这一层上打一下标记,查询时查询(dep_i + w_i)这层的子树和
    下行类似,在终点(ed)处打标记,柿子列出来发现是(Len - (dep_ed - dep_i) = w_i),其中(Len)指跑步的路径长度
    移项一下是(Len - dep_ed = w_i - dep_i),然后同上打一下标记,注意为了保证(LCA)处的贡献可以正常统计,上行和下行的(-1)标记有一个要打在(fa[LCA])
    对于每一层子树和的统计,我们对着每一个(dep)开一棵线段树,然后动态开点一下,查询子树和就是(dfs)序上连续一段

    #include<bits/stdc++.h>
    #define N (600000 + 10)
    using namespace std;
    inline int read() {
    	int cnt = 0, f = 1; char c = getchar();
    	while (!isdigit(c)) {if (c == '-') f = -f; c = getchar();}
    	while (isdigit(c)) {cnt = (cnt << 3) + (cnt << 1) + (c ^ 48); c = getchar();}
    	return cnt * f;
    }
    int n, m, u, v, w[N], tot;
    struct node {int s, t, l;} a[N];
    int nxt[N], first[N], to[N];
    void add(int x, int y) {nxt[++tot] = first[x], first[x] = tot, to[tot] = y;}
    int fa[N], siz[N], dep[N], son[N], top[N], num[N], idx;
    void dfs1(int cur, int father) {
    	fa[cur] = father, dep[cur] = dep[father] + 1, siz[cur] = 1;
    	for (register int i = first[cur]; i; i = nxt[i]) {
    		int v = to[i];
    		if (v == father) continue;
    		dfs1(v, cur);
    		siz[cur] += siz[v];
    		if (siz[son[cur]] < siz[v]) son[cur] = v;
    	}
    }
    void dfs2(int cur, int tp) {
    	top[cur] = tp, num[cur] = ++idx;
    	if (son[cur]) dfs2(son[cur], tp);
    	for (register int i = first[cur]; i; i = nxt[i]) {
    		int v = to[i];
    		if (num[v]) continue;
    		dfs2(v, v);
    	}
    }
    int lca(int u, int v) {
    	while (top[u] != top[v]) {
    		if (dep[top[u]] < dep[top[v]]) swap(u, v);
    		u = fa[top[u]];
    	} return dep[u] < dep[v] ? u : v;
    }
    int ans[N];
    int now, rt[N], ls[N * 20], rs[N * 20], val[N * 20];
    void modify(int &x, int l, int r, int pos, int v) {
    	if (!x) x = ++now; val[x] += v; if (l == r) return;
    	int mid = (l + r) >> 1;
    	if (pos <= mid) modify(ls[x], l, mid, pos, v);
    	else modify(rs[x], mid + 1, r, pos, v);
    }
    int query(int x, int l, int r, int L, int R) {
    	if (!x) return 0; if (L <= l && R >= r) return val[x];
    	int mid = (l + r) >> 1, ans = 0;
    	if (L <= mid) ans += query(ls[x], l, mid, L, R);
    	if (R > mid) ans += query(rs[x], mid + 1, r, L, R);
    	return ans;
    }
    void clear() {for (register int i = 1; i <= now; ++i) ls[i] = rs[i] = val[i] = 0; memset(rt, 0, sizeof rt); now = 0;}
    int main() {
    	n = read(), m = read();
    	for (register int i = 1; i < n; ++i) {
    		u = read(), v = read();
    		add(u, v), add(v, u);
    	}
    	dfs1(1, 0), dfs2(1, 1);
    	for (register int i = 1; i <= n; ++i) w[i] = read();
    	for (register int i = 1; i <= m; ++i) a[i].s = read(), a[i].t = read(), a[i].l = lca(a[i].s, a[i].t);
    	/*-----------go up------------*/ 
    	for (register int i = 1; i <= m; ++i) {
    		modify(rt[dep[a[i].s]], 1, n, num[a[i].s], 1);
    		if (fa[a[i].l]) modify(rt[dep[a[i].s]], 1, n, num[fa[a[i].l]], -1);
    	}
    	for (register int i = 1; i <= n; ++i) ans[i] = query(rt[dep[i] + w[i]], 1, n, num[i], num[i] + siz[i] - 1);
    	/*----------go up-------------*/
    	clear();
    	/*----------go down -----------*/
    	for (register int i = 1; i <= m; ++i) {
    		int Len = dep[a[i].t] + dep[a[i].s] - 2 * dep[a[i].l];
    		int cur = Len - dep[a[i].t] + n;
    		modify(rt[cur], 1, n, num[a[i].t], 1);
    		modify(rt[cur], 1, n, num[a[i].l], -1);
    	}
    	for (register int i = 1; i <= n; ++i) ans[i] += query(rt[w[i] - dep[i] + n], 1, n, num[i], num[i] + siz[i] - 1);
    	for (register int i = 1; i <= n; ++i) printf("%d ", ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    spring scheduled单线程和多线程使用过程中的大坑!!不看到时候绝对后悔!!
    在idea中配置 gitignore忽略文件(一)
    Cron表达式范例:每隔5秒执行一次:*/5 * * * * ?
    软件——protel 的pcb电路图制作
    【纪中受难记】——Day21:调整心态
    2019第十届蓝桥杯C/C++ B组省赛 —— 第二题:年号字串
    2019第十届蓝桥杯C/C++ B组省赛 —— 第三题:数列求值
    2019第十届蓝桥杯C/C++ B组省赛 —— 第三题:数列求值
    2019第十届蓝桥杯C/C++ B组省赛 —— 第一题:组队
    2019第十届蓝桥杯C/C++ B组省赛 —— 第一题:组队
  • 原文地址:https://www.cnblogs.com/kma093/p/11809138.html
Copyright © 2011-2022 走看看