zoukankan      html  css  js  c++  java
  • BZOJ 5477: 星际穿越

    当初随便出的一道 思博题 竟然被交换到了八中 QAQ

    然后就上了 BZOJ 。。。作为原作者还是把原来写的详细题解放出来吧 qwq

    题意

    (n) 个点的数,每个点初始有权值 (v_i) ,需要支持 (m) 次操作。

    动态单点加权值,查询子树内点对的距离和。

    题解

    5pts

    观察到第 (9) 个点,只有修改,没有询问。只需要正确打开文件并且不输出任何字符即可。

    (注意暴力不能 (RE) 或者 (MLE)(TLE) 才能 艰难地 拿到这个分)

    15pts

    对于 (n, m le 50) 的点,每次直接修改点权。

    询问的时候暴力枚举子树中的点对,然后跳 (Lca) 计算距离。

    复杂度是 (O(mn^3)) 的。

    30pts

    发现暴力枚举点对的时候不需要重新跳 (Lca) ,直接从每个点 (Bfs) or (Dfs) 即可。

    复杂度是 (O(mn^2)) 的。

    45pts

    考虑把暴力这颗子树弄出来,就变成求树上两两点对带权距离和的经典问题。
    如果工业一点就可以写个点分治,复杂度就是 (O(mn log n)) 的,常数优秀应该可以得到这些分。

    但其实没有这么麻烦,如果是边权的话,每个边被多少条路径经过,其实就是两边子树 (size) (子树大小)的乘积,也就是 (size * (tot - size + 1))

    但这题权值是在点上的,所以要枚举它每一个儿子顺次计算,就是每个点的 (size) 和前面的 (size) 之和的乘积,最后加上子树内外的 (size) 之积就行了。然后复杂度就是 (O(nm)) 的。

    还有一种一遍 (dp) 一遍换根的做法就不多赘述了,复杂度也是 (O(nm)) 的。

    55pts

    其实刚刚那个 (O(nm log n)) 或者 (O(nm)) 的算法也可以通过 (10, 11) 号点的...

    60pts

    但对于只有询问的点,应该是有更好的做法,利用两点距离 (dis_{a, b} = dep_a + dep_b - dep_{lca(a, b)} imes 2 + v_{lca(a, b)}) 这个经典计算方式。

    考虑每个点的 (dep) 计算了多少次,以及它作为 (lca) 时计算了多少次 (dep)(v)

    我们推导式子:

    [egin{aligned} ans &= sum_{u in child_p} sum_{v in child_p, v ge u} dep_u + dep_v - dep_{lca(a, b)} * 2 + v_{lca(a, b)} \ &= (sum_{u in child_p} dep_u) imes (size_p + 1) + sum_{u in child_p} (v_u - dep_u * 2) * coef_u end{aligned} ]

    此处 (coef_u)(u) 作为 (lca) 出现的次数。至于求这个,可以依次考虑它的每个儿子,系数就是每个儿子的 (sz) 乘上前面所有 (sz) 的和(一开始要算上 (u) 点)。

    我们用 (Dfs) 预处理就行了,记下当前的 (displaystyle sum_{u in child_p} dep_u) 的值,以及 (displaystyle sum_{u in child_p} (v_u - dep_u * 2) * coef_u) 即可在 (O(n)) 的时间里预处理出所有点的答案。

    80pts

    (u_i = v_i - 1) :直接考虑每个点被多少个区间覆盖即可,用线段树支持动态修改和查询。

    (u_i = 1) :分类讨论即可。询问的时候 (p = 2)(u ot = 1) 的时候直接输出 (v_p)(u = 1) 的时候也可以十分轻易地直接维护答案。

    这些点只是为了凑部分分的

    100pts

    方法一

    至于正解,我们考虑动态维护前面 (60pts) 的式子。

    我们发现每次只需要动态查询子树的 (sum dep_u) (带权深度)的和,以及 (displaystyle sum_{u in child_p} (v_u - dep_u * 2) * coef_u) 就行了。

    每次修改单点权值,等价于修改子树内所有点的带权深度和,我们用线段树支持子树修改就行了,然后询问的时候就子树询问。

    至于 (*~coef_u) ,我们对于线段树每个区间维护 (coef) 的和,每次给线段树区间加的时候,把 (sum) 加上 (coef imes x) 就行了,复杂度就是 (O(m log n)) 的。

    方法二

    考虑前面 (45pts) 其中的一种做法,考虑一个点被多少条路径穿过。

    我们先假设只询问全树,那么可以用树状数组维护每个点的系数。那么就可以直接单点修改,区间查询就行了。

    如果是询问子树的话,也是很简单的,我们减去经过子树 (u) 内点的路径的多余贡献就行了。具体来说,就是子树 (u) 中每个点的 (size) 乘上子树 (u) 外的点数 (n - size_u) 就行了。

    同样这个也可以用树状数组实现,十分的好写好调。

    一些有意思的东西

    这题应该是一道原创 送分 题,其实思路十分的简单,体现了出题人的良心。

    考察了对于代码的实现以及对于数据结构的实现。

    就是代码实现略微有点细节,利用了一个差分的小 (trick)

    在出完这道题后,找 Hometown 验题的时候,他告诉我了方法二,简单好写常数小,发现 (std) 又双叒叕被踩了。。。 果然我只能出思博题啊!

    代码

    方法一

    这是出题人一开始想到的一个 sb 方法,常数大,还难写。。

    /**************************************************************
        Problem: 5477
        User: zjp_shadow
        Language: C++
        Result: Accepted
        Time:8816 ms
        Memory:67256 kb
    ****************************************************************/
     
    #include <bits/stdc++.h>
     
    #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
    #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
    #define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
    #define Set(a, v) memset(a, v, sizeof(a))
    #define Cpy(a, b) memcpy(a, b, sizeof(a))
    #define debug(x) cout << #x << ": " << (x) << endl
    #define pb push_back
     
    using namespace std;
     
    template<typename T> inline bool chkmin(T &a, T b) {return b < a ? a = b, 1 : 0;}
    template<typename T> inline bool chkmax(T &a, T b) {return b > a ? a = b, 1 : 0;}
     
    inline int read() {
        int x(0), sgn(1); char ch(getchar());
        for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
        for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
        return x * sgn;
    }
     
    typedef long long ll;
     
    const int N = 3e5 + 1e3, Mod = 1e9 + 7;
     
    vector<int> G[N]; int id[N];
     
    inline int Plus(int a, int b) {
        return (a += b) >= Mod ? a - Mod : a;
    }
     
    #define lson o << 1, l, mid
    #define rson o << 1 | 1, mid + 1, r
     
    template<int Maxn>
    struct Segment_Tree {
     
        int coef[Maxn], sumv[Maxn], tag[Maxn];
     
        inline void Modify(int o, int uv) {
            tag[o] = Plus(tag[o], uv);
            sumv[o] = (sumv[o] + 1ll * coef[o] * uv) % Mod;
        }
     
        inline void Push_Down(int o) {
            if (tag[o])
                Modify(o << 1, tag[o]), Modify(o << 1 | 1, tag[o]), tag[o] = 0;
        }
     
        inline void Push_Up(int o) {
            sumv[o] = Plus(sumv[o << 1], sumv[o << 1 | 1]);
        }
     
        void Build(int o, int l, int r, int *base, int *cur) {
            if (l == r) { sumv[o] = 1ll * (coef[o] = base[id[l]]) * cur[id[l]] % Mod; return ; }
            int mid = (l + r) >> 1;
            Build(lson, base, cur); Build(rson, base, cur);
            Push_Up(o); coef[o] = Plus(coef[o << 1], coef[o << 1 | 1]);
        }
     
        void Update(int o, int l, int r, int ul, int ur, int uv) {
            if (ul <= l && r <= ur) { Modify(o, uv); return ; }
            int mid = (l + r) >> 1; Push_Down(o);
            if (ul <= mid) Update(lson, ul, ur, uv);
            if (ur > mid) Update(rson, ul, ur, uv); Push_Up(o);
        }
     
        int Query(int o, int l, int r, int ql, int qr) {
            if (ql <= l && r <= qr) return sumv[o];
            int mid = (l + r) >> 1, res = 0; Push_Down(o);
            if (ql <= mid) res = Plus(res, Query(lson, ql, qr));
            if (qr > mid) res = Plus(res, Query(rson, ql, qr));
            Push_Up(o); return res;
        }
     
    };
     
    #undef lson
    #undef rson
     
    Segment_Tree<N << 2> T1, T2, T3;
     
    int coef[N], dfn[N], efn[N], sz[N], dep[N];
     
    void Dfs_Init(int u = 1, int fa = 0) {
        static int clk = 0;
        dep[u] = dep[fa] + 1;
        id[dfn[u] = ++ clk] = u;
     
        coef[u] = sz[u] = 1;
        Rep (i, G[u].size()) {
            int v = G[u][i];
            if (v != fa) {
                Dfs_Init(v, u);
                coef[u] = (coef[u] + 1ll * sz[u] * sz[v]) % Mod;
                sz[u] += sz[v];
            }
        }
        efn[u] = clk;
    }
     
    int main () {
     
        //freopen ("transport.in", "r", stdin);
        //freopen ("transport.out", "w", stdout);
     
        int n = read(), m = read();
     
        For (i, 1, n - 1) {
            int u = read(), v = read();
            G[u].pb(v); G[v].pb(u);
        }
     
        Dfs_Init();
     
        int I[N]; For (i, 1, n) I[i] = 1;
     
        T1.Build(1, 1, n, I, dep);
        T2.Build(1, 1, n, coef, dep);
        T3.Build(1, 1, n, coef, I);
     
        For (i, 1, m) {
            int opt = read(), pos = read();
            if (opt == 1) {
                int val = read();
                T1.Update(1, 1, n, dfn[pos], efn[pos], val);
                T2.Update(1, 1, n, dfn[pos], efn[pos], val);
                T3.Update(1, 1, n, dfn[pos], dfn[pos], val);
            } else {
     
                long long ans = 
                    1ll * T1.Query(1, 1, n, dfn[pos], efn[pos]) * (sz[pos] + 1)
                    - T2.Query(1, 1, n, dfn[pos], efn[pos]) * 2ll
                    + T3.Query(1, 1, n, dfn[pos], efn[pos]);
     
                printf ("%lld
    ", (ans % Mod + Mod) % Mod);
            }
        }
     
        return 0;
    }
    

    方法二

    简单的树状数组解法 QAQ

    #include <bits/stdc++.h>
    
    #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
    #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
    #define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
    #define Set(a, v) memset(a, v, sizeof(a))
    #define Cpy(a, b) memcpy(a, b, sizeof(a))
    #define debug(x) cout << #x << ": " << (x) << endl
    #define pb push_back
    
    using namespace std;
    
    typedef long long ll;
    
    template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
    template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }
    
    inline int read() {
    	int x(0), sgn(1); char ch(getchar());
    	for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
    	for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
    	return x * sgn;
    }
    
    const int N = 3e5 + 1e3, Mod = 1e9 + 7;
    
    vector<int> G[N]; 
    
    int n, dfn[N], efn[N], sz[N], coef[N];
    
    inline int Plus(int a, int b) {
    	return (a += b) >= Mod ? a - Mod : a;
    }
    
    #define lowbit(x) (x & -x)
    template<int Maxn>
    struct Fenwick_Tree {
    
    	int sumv[Maxn];
    
    	inline void Update(int pos, int uv) {
    		for (; pos <= n; pos += lowbit(pos)) 
    			sumv[pos] = Plus(sumv[pos], uv);
    	}
    
    	inline int Query(int pos) {
    		int res = 0;
    		for (; pos; pos -= lowbit(pos))
    			res = Plus(res, sumv[pos]);
    		return res;
    	}
    
    	inline int Query(int l, int r) {
    		return Query(r) - Query(l - 1);
    	}
    
    };
    
    Fenwick_Tree<N> T1, T2;
    
    void Dfs_Init(int u = 1, int fa = 0) {
    	static int clk = 0;
    	dfn[u] = ++ clk;
    
    	sz[u] = coef[u] = 1;
    	Rep (i, G[u].size()) {
    		int v = G[u][i];
    		if (v != fa) {
    			Dfs_Init(v, u); 
    			coef[u] = (coef[u] + 1ll * sz[u] * sz[v]) % Mod;
    			sz[u] += sz[v];
    		}
    	}
    	coef[u] = (coef[u] + 1ll * sz[u] * (n - sz[u])) % Mod;
    
    	T1.Update(dfn[u], coef[u]);
    	T2.Update(dfn[u], sz[u]); efn[u] = clk;
    }
    
    int main () {
    
    ////freopen ("transport.in", "r", stdin);
    ////freopen ("transport.out", "w", stdout);
    
    	n = read(); int m = read();
    
    	For (i, 1, n - 1) {
    		int u = read(), v = read();
    		G[u].pb(v); G[v].pb(u);
    	}
    
    	Dfs_Init();
    
    	For (i, 1, m) {
    		int opt = read(), pos = read();
    		if (opt == 1) {
    			int val = read();
    			T1.Update(dfn[pos], 1ll * val * coef[pos] % Mod);
    			T2.Update(dfn[pos], 1ll * val * sz[pos] % Mod);
    		} else {
    			long long ans = 
    				T1.Query(dfn[pos], efn[pos]) 
    				- 1ll * T2.Query(dfn[pos], efn[pos]) * (n - sz[pos]);
    			printf ("%lld
    ", (ans % Mod + Mod) % Mod);
    		}
    	}
    
    	return 0;
    
    }
    
  • 相关阅读:
    ZOJ 1002 Fire Net
    Uva 12889 One-Two-Three
    URAL 1881 Long problem statement
    URAL 1880 Psych Up's Eigenvalues
    URAL 1877 Bicycle Codes
    URAL 1876 Centipede's Morning
    URAL 1873. GOV Chronicles
    Uva 839 Not so Mobile
    Uva 679 Dropping Balls
    An ac a day,keep wa away
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/10374574.html
Copyright © 2011-2022 走看看