ACM-ICPC 2018 徐州赛区网络预赛 H. Ryuji doesn’t want to study 线段树+带权查询


ACM-ICPC 2018 徐州赛区网络预赛 H. Ryuji doesn’t want to study


标签

  • 线段树
  • 带权查询

简明题意

  • 给一个序列,需要支持两种操作:
    1. 查询:查询L-R区间, a [ l ] × L + a [ l + 1 ] × ( L − 1 ) + ⋯ + a [ r − 1 ] × 2 + a [ r ] a[l]×L+a[l+1]×(L−1)+⋯+a[r−1]×2+a[r] a[l]×L+a[l+1]×(L1)++a[r1]×2+a[r],其中L定义为查询的区间的长度
    2. 修改:将某个值修改为x

思路

  • 查询的元素带权了,权值跟所查的右端点相关。怎么办呢。我的做法是线段树维护两种信息,一个是L-R的和,另一个是,从1开始每个元素的n-r+1倍的和,这样维护的话,我们区间查询,比如区间长度是6,查询3-5,用维护的第二种信息查询出来是 4 ∗ a [ 3 ] + 3 ∗ a [ 4 ] + 2 ∗ a [ 5 ] 4*a[3]+3*a[4]+2*a[5] 4a[3]+3a[4]+2a[5],维护的第一种信息查询出来是 a [ 3 ] + a [ 4 ] + a [ 5 ] a[3]+a[4]+a[5] a[3]+a[4]+a[5],将这两个直接相减就得到了想要的答案。当然,如果查询的区间不同,维护的第二种信息的右端点的倍数不一样,减去的值也应该是第一种信息的若干倍

注意事项


总结

  • 带权查询有的时候可以多维护一种信息,从而得到正确的关系

AC代码

#include<cstdio>
#include<algorithm>
using namespace std;

const long long maxn = 1e5 + 10;

long long n, m, a[maxn];

struct Node
{
   long long l, r;
   long long sum_j, sum_single;
};

Node tree[maxn * 4];

void update(long long o)
{
   if (tree[o].l != tree[o].r)
   {
   	tree[o].sum_j = tree[o * 2].sum_j + tree[o * 2 + 1].sum_j;
   	tree[o].sum_single = tree[o * 2].sum_single + tree[o * 2 + 1].sum_single;
   }
}

void build(long long o, long long l, long long r)
{
   tree[o].l = l, tree[o].r = r;
   if (l == r)
   {
   	tree[o].sum_single = a[l];
   	tree[o].sum_j = (n - r + 1) * a[l];
   	return;
   }

   long long mid = (l + r) / 2;
   build(o * 2, l, mid);
   build(o * 2 + 1, mid + 1, r);

   update(o);
}

long long ask(long long o, long long l, long long r, long long k)
{
   if (tree[o].l == l && tree[o].r == r)
   	return tree[o].sum_j - k * tree[o].sum_single;
   
   long long mid = (tree[o].l + tree[o].r) / 2;
   if (r <= mid)
   	return ask(o * 2, l, r, k);
   else if (l > mid)
   	return ask(o * 2 + 1, l, r, k);
   else
   	return ask(o * 2, l, mid, k) + ask(o * 2 + 1, mid + 1, r, k);
}

void change(long long o, long long x, long long c)
{
   if (tree[o].l == tree[o].r)
   {
   	tree[o].sum_single = c;
   	tree[o].sum_j = (n - tree[o].r + 1) * c;
   	return;
   }

   long long mid = (tree[o].l + tree[o].r) / 2;
   if (x <= mid)
   	change(o * 2, x, c);
   else
   	change(o * 2 + 1, x, c);

   update(o);
}

void solve()
{
   scanf("%lld%lld", &n, &m);
   for (long long i = 1; i <= n; i++)
   	scanf("%lld", &a[i]);

   build(1, 1, n);

   while (m--)
   {
   	long long opt;
   	scanf("%lld", &opt);

   	if (opt == 1)
   	{
   		long long l, r;
   		scanf("%lld%lld", &l, &r);

   		printf("%lld\n", ask(1, l, r, n - r));
   	}
   	else
   	{
   		long long x, c;
   		scanf("%lld%lld", &x, &c);

   		change(1, x, c);
   	}
   }
}

int main()
{
   freopen("Testin.txt", "r", stdin);
   solve();
   return 0;
}

双倍经验

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值