• 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;
    
    }
    
  • 相关阅读:
    门头沟PHP,java编程兴趣小组欢迎大家的加入。
    mysql数据库怎么存入emoji表情,更改utf8mb4后为什么出现全是问号
    Laravel发送邮件配置-问题解决
    php版本低更换php版本-问题以解决
    极光客户互动云java post请求
    【问题解决】增加https后 phpcms 分页错误
    阿里云阿里免费ssl wap网站在手机微信、手机浏览器无法访问
    Kali Linux on Android # 实测:小米2s离线安装Kali Linux
    3-1 LVS-NAT集群
    使用LVM方式安装Ubuntu 16.04
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/10374574.html
Copyright © 2020-2023  润新知