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;
    }
    
  • 相关阅读:
    Postman使用教程
    CAD和ArcGIS转换 矢量配准
    SAP CRM Advanced search和Simple search里Max hit表现行为的差异
    SAP CRM Product simple search的启用步骤
    如何快速定位SAP CRM订单应用(Order Application)错误消息抛出的准确位置
    如何动态修改SAP CRM WebClient UI表格栏的宽度
    如何在SAP CRM WebClient UI里创建web service并使用ABAP消费
    如何处理SAP CRM Web Service错误
    如何使用SAP CRM WebClient UI实现一个类似新浪微博的字数统计器
    如何开启SAP CRM基于WORD模板创建附件的功能
  • 原文地址:https://www.cnblogs.com/kma093/p/11809138.html
Copyright © 2011-2022 走看看