zoukankan      html  css  js  c++  java
  • 题解【洛谷P2221】[HAOI2012]高速公路

    题面

    我们需要一棵支持区间加、区间询问所有子区间和的线段树。

    对于询问,假设询问区间是 ([l,r]),答案即为:

    [dfrac{sumlimits_{i=l}^rsumlimits_{j=i}^r dis_{i,j}}{inom{r-l+1}{2}} ]

    分母可以直接 (mathcal{O}(1)) 算;

    对于分子,我们发现点之间的距离不好用线段树维护,于是我们把边转成点,然后考虑计算每一条边对答案的贡献:

    [egin{aligned}sum_{i=l}^r v_i(r-i+1)(i-l+1) &= sumlimits_{i=l}^r v_i(ri-lr+r-i^2+il-i+i-l+1) \ &= sumlimits_{i=l}^r -i^2v_i+iv_i(r+l)+v_i(-lr+r-l+1)end{aligned}]

    (s_1=sum v_i)(s_2=sum iv_i)(s_3=sum i^2v_i)

    答案即为:

    [egin{aligned} -s3+s_2(r+l)+s_1(-lr+r-l+1)end{aligned} ]

    可以开 (3) 棵线段树解决。

    然后考虑怎么修改。

    (s_1):直接区间修改即可。

    (s_2)(sum i(v_i+k)=sum iv_i+ik),可以维护一个前缀和 (s_4) 记录 (sum i)

    (s_3)(sum i^2(v_i+k)=sum i^2v_i+i^2k),维护一个前缀和 (s_5) 记录 (sum i^2)

    推荐练习:[SDOI2017]相关分析

    代码:

    #include <bits/stdc++.h>
    #define DEBUG fprintf(stderr, "Passing [%s] line %d
    ", __FUNCTION__, __LINE__)
    #define File(x) freopen(x".in","r",stdin); freopen(x".out","w",stdout)
    #define int long long
    
    using namespace std;
    
    typedef long long LL;
    typedef pair <int, int> PII;
    typedef pair <int, PII> PIII;
    
    template <typename T>
    inline T gi()
    {
    	T f = 1, x = 0; char c = getchar();
    	while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
    	while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return f * x;
    }
    
    const int INF = 0x3f3f3f3f, N = 100003, M = N << 1;
    
    int n, m;
    LL sum[4][N << 2], lz[4][N << 2];
    LL pr[3][N << 2];
    
    inline int ls(int p) {return p << 1;}
    inline int rs(int p) {return p << 1 | 1;}
    
    inline void pushup(int p)
    {
    	for (int i = 1; i <= 3; i+=1) sum[i][p] = sum[i][ls(p)] + sum[i][rs(p)];
    }
    
    inline void mark(int l, int r, int val, int p)
    {
    	sum[1][p] += 1ll * val * (r - l + 1),
    		sum[2][p] += 1ll * val * (pr[1][r] - pr[1][l - 1]),
    		sum[3][p] += 1ll * val * (pr[2][r] - pr[2][l - 1]);
    	lz[1][p] += val, lz[2][p] += val, lz[3][p] += val;
    }
    
    inline void pushdown(int l, int r, int p)
    {
    	int mid = (l + r) >> 1;
    	if (lz[1][p])
    	{
    		sum[1][ls(p)] += 1ll * lz[1][p] * (mid - l + 1),
    			sum[1][rs(p)] += 1ll * lz[1][p] * (r - mid);
    		lz[1][ls(p)] += lz[1][p],
    			lz[1][rs(p)] += lz[1][p];
    		lz[1][p] = 0;
    	}
    	if (lz[2][p])
    	{
    		sum[2][ls(p)] += 1ll * lz[2][p] * (pr[1][mid] - pr[1][l - 1]),
    			sum[2][rs(p)] += 1ll * lz[2][p] * (pr[1][r] - pr[1][mid]);
    		lz[2][ls(p)] += lz[2][p],
    			lz[2][rs(p)] += lz[2][p];
    		lz[2][p] = 0;
    	}
    	if (lz[3][p])
    	{
    		sum[3][ls(p)] += 1ll * lz[3][p] * (pr[2][mid] - pr[2][l - 1]),
    			sum[3][rs(p)] += 1ll * lz[3][p] * (pr[2][r] - pr[2][mid]);
    		lz[3][ls(p)] += lz[3][p],
    			lz[3][rs(p)] += lz[3][p];
    		lz[3][p] = 0;
    	}
    }
    
    void modify(int ql, int qr, int val, int l, int r, int p)
    {
    	if (ql <= l && r <= qr)
    	{
    		mark(l, r, val, p);
    		return;
    	}
    	pushdown(l, r, p);
    	int mid = (l + r) >> 1;
    	if (ql <= mid) modify(ql, qr, val, l, mid, ls(p));
    	if (qr > mid) modify(ql, qr, val, mid + 1, r, rs(p));
    	pushup(p);
    }
    
    LL getans(int id, int ql, int qr, int l, int r, int p)
    {
    	if (ql <= l && r <= qr) return sum[id][p];
    	pushdown(l, r, p);
    	int mid = (l + r) >> 1;
    	LL sum = 0;
    	if (ql <= mid) sum += getans(id, ql, qr, l, mid, ls(p));
    	if (qr > mid) sum += getans(id, ql, qr, mid + 1, r, rs(p));
    	return sum;
    }
    
    inline LL query(int l, int r)
    {
    	LL s1 = getans(1, l, r, 1, n - 1, 1), s2 = getans(2, l, r, 1, n - 1, 1), s3 = getans(3, l, r, 1, n - 1, 1);
    	return s2 * (l + r) + s1 * (r + 1 - l - l * r) - s3;
    }
    
    signed main()
    {
    	//File("");
    	n = gi <int> (), m = gi <int> ();
    	for (int i = 1; i <= n; i+=1)
    		pr[1][i] = pr[1][i - 1] + i, pr[2][i] = pr[2][i - 1] + 1ll * i * i;
    	while (m--)
    	{
    		char op[3];
    		scanf("%s", op);
    		int l = gi <int> (), r = gi <int> () - 1, v;
    		if (op[0] == 'C')
    		{
    			v = gi <int> ();
    			modify(l, r, v, 1, n - 1, 1);
    		}
    		else
    		{
    			if (l > r) {puts("0/1"); continue;}
    			LL len = 1ll * (r - l + 1) * (r - l + 2) / 2;
    			LL ans = query(l, r);
    			LL G = __gcd(ans, len);
    			ans /= G, len /= G;
    			printf("%lld/%lld
    ", ans, len);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    LeetCode 21. 合并两个有序链表
    LeetCode 20. 有效的括号
    LeetCode 19. 删除链表的倒数第N个节点
    AI
    http
    IP地址
    wiodows /linux CMD
    git
    AI
    JS常用的获取值和设值的方法
  • 原文地址:https://www.cnblogs.com/xsl19/p/13620010.html
Copyright © 2011-2022 走看看