线段树差分(线段树存储等差数列)

Ground Defense

时间限制: 1 Sec 内存限制: 128 MB
题目描述
You are a denizen of Linetopia, whose n major cities happen to be equally spaced along an east-west line. In fact, they are often numbered in order from 1 to n, where 1 is the westmost city and n is the eastmost city.
Linetopia was a lovely place to live until forces from neighboring Trapez invaded. As part of Linetopia’s Shielding Lives and Protecting Citizens initiative, you have been called upon to process information about Trapezoid troop movements so we can determine which cities have been hardest hit and know where to send reinforcements.
Linetopia intelligence has discovered that the Trapezoid forces are attacking in the following pattern. They are sending massive aircraft to drop troops on Linetopia cities. Each aircraft starts at some city i, dropping s soldiers. The aircraft then proceeds to fly either east or west. Each time it flies over another city, it drops a more soldiers than it dropped on the previous city it passed. After performing d drops, the aircraft returns to Trapez to resupply.
You will be receiving intel updates that inform you of the specs of each Trapezoid aircraft passing over Linetopia. You want to answer queries that ask how many Trapezoid troops have been dropped on a particular city. Are you up to the task?

输入
The first line of input contains a single integer T (1 ≤ T ≤ 10), the number of test cases. The first line of each test case contains two integers: m (1 ≤ m ≤ 10,000), the number of updates and queries and n (1 ≤ n ≤ 500,000), the number of cities in Linetopia.
The next m lines of input are either updates or queries. Update lines begin with a capital U, then contain either a capital E (east) or W (west) to indicate direction, and then contain four integers i (1 ≤ i ≤ n), s (1 ≤ s ≤ 10,000), a (0 ≤ a ≤ 10,000), and d (1 ≤ d ≤ n). These integers signify the starting city, the starting number of soldiers, the increase in soldiers per city, and the number of drops, respectively. You can assume d never results in an aircraft flying to the west of city 1 or to the east of city n.
Query lines begin with a capital Q, and then contain a single integer i (1 ≤ i ≤ n) indicating the city being queried.

输出
For each query in the input, output a single line containing the number of Trapezoid troops dropped in that city.

样例输入
复制样例数据
1
8 3
U E 1 5 2 3
Q 1
Q 2
Q 3
U W 3 10 10 2
Q 1
Q 2
Q 3
样例输出
5
7
9
5
27
19

用线段树存储等差数列只要存储等差数列的首项和公差就好了。
考虑使用两个线段树分别存储首项和公差。公差需要考虑加几次的问题,这里直接将公差存储在线段树的区间上,用线段树求前缀和,就相当于加上所需次数的公差。
语言表述不明确,参照下图说明:
在这里插入图片描述
求给定位置 i 的值就是将基线段树 i 处的值加上公差线段树从1到 i 的区间和。上图中红色的-6是为了平衡前面的公差,防止前面的等差数列对后面区间的值产生影响。

要在给定的区间上加上一个等差数列,操作就是在基线段树的给定区间上全部加上等差数列的首项,然后在公差线段树上从首项后面一项开始到区间末尾结束的位置全部加上公差,还应注意在最后一项后面加上一个值抵消前面所有的公差,避免前面的公差对后面产生影响。

如果是加入一个从右向左的递增等差数列,就等于加入一个从左向右的递减等差数列,操作基本相同。

这样就把线段树给定区间加等差数列 转化成了 两棵线段树分别做区间加相同值。

代码如下:(代码很长是因为我把相同是线段树模板抄了两遍,不必害怕,可以直接看main函数的操作,前面的部分熟悉普通线段树区间加值和求区间和操作模板即可)

#include <bits/stdc++.h>

using namespace std;
const int MAXN = 500000+1;
int n,req,sizee;

struct segmenttree
{
    int l,r;
    long long sum,add;
#define l(x) tree[x].l
#define r(x) tree[x].r
#define sum(x) tree[x].sum
#define add(x) tree[x].add
} tree[MAXN*4];

void build(long long p,long long l,long long r)
{
    l(p) = l;
    r(p) = r;
    sum(p) = 0;
    add(p) = 0;
    if(l == r)
    {
        return;
    }
    long long mid = (l+r)/2;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
}

void spread(long long p)
{
    if(add(p))
    {
        sum(p*2) += add(p)*(r(p*2) - l(p*2) +1);
        sum(p*2+1) += add(p)*(r(p*2+1) - l(p*2+1)+1);
        add(p*2) += add(p);
        add(p*2+1) += add(p);
        add(p) = 0;
    }
}

void change(long long p,long long l,long long r,long long d)
{
    if(l<=l(p)&&r>=r(p))
    {
        sum(p) += (long long)d*(r(p) - l(p) + 1);
        add(p) += d;
        return;
    }
    spread(p);
    long long mid = (l(p)+r(p))/2;
    if(l<= mid)
    {
        change(p*2,l,r,d);
    }
    if(r>mid)
    {
        change(p*2+1,l,r,d);
    }
    sum(p) = sum(p*2)+sum(p*2+1);
}

long long ask(long long p,long long l,long long r)
{
    if(l<=l(p)&&r>=r(p))
    {
        return sum(p);
    }
    spread(p);
    long long mid = (l(p)+r(p))/2;
    long long val = 0;
    if(l<=mid)
    {
        val+=ask(p*2,l,r);
    }
    if(r>mid)
    {
        val+=ask(p*2+1,l,r);
    }
    return val;
}

struct segmenttree1
{
    long long l,r;
    long long sum,add;
#define l1(x) tree1[x].l
#define r1(x) tree1[x].r
#define sum1(x) tree1[x].sum
#define add1(x) tree1[x].add
} tree1[MAXN*4];

void build1(long long p,long long l,long long r)
{
    l1(p) = l;
    r1(p) = r;
    sum1(p) = 0;
    add1(p) = 0;
    if(l == r)
    {
        return;
    }
    long long mid = (l+r)/2;
    build1(p*2,l,mid);
    build1(p*2+1,mid+1,r);
}

void spread1(long long p)
{
    if(add1(p))
    {
        sum1(p*2) += add1(p)*(r1(p*2) - l1(p*2) +1);
        sum1(p*2+1) += add1(p)*(r1(p*2+1) - l1(p*2+1)+1);
        add1(p*2) += add1(p);
        add1(p*2+1) += add1(p);
        add1(p) = 0;
    }
}

void change1(long long p,long long l,long long r,long long d)
{
    if(l<=l1(p)&&r>=r1(p))
    {
        sum1(p) += (long long)d*(r1(p) - l1(p) + 1);
        add1(p) += d;
        return;
    }
    spread1(p);
    long long mid = (l1(p)+r1(p))/2;
    if(l<= mid)
    {
        change1(p*2,l,r,d);
    }
    if(r>mid)
    {
        change1(p*2+1,l,r,d);
    }
    sum1(p) = sum1(p*2)+sum1(p*2+1);
}

long long ask1(long long p,long long l,long long r)
{
    if(l<=l1(p)&&r>=r1(p))
    {
        return sum1(p);
    }
    spread1(p);
    long long mid = (l1(p)+r1(p))/2;
    long long val = 0;
    if(l<=mid)
    {
        val+=ask1(p*2,l,r);
    }
    if(r>mid)
    {
        val+=ask1(p*2+1,l,r);
    }
    return val;
}

int main()
{
    scanf("%d",&n);
    getchar();
    for(long long ii = 1; ii <= n; ii++)
    {
        scanf("%d %d",&req,&sizee);
        build(1,1,sizee);
        build1(1,1,sizee);
        for(long long i = 1; i<=req; i++)
        {
            char s = ' ';
            while(s == ' '||s == '\n')
            {
                scanf("%c",&s);
            }
            if(s == 'U')
            {
                char ss = ' ';
                while(ss == ' '||ss == '\n')
                    scanf("%c",&ss);
                long long startt,fromm,stepp,lenn;
                scanf("%lld%lld%lld%lld",&startt,&fromm,&stepp,&lenn);
                if(ss == 'E')
                {
                    change1(1,startt,startt+lenn - 1,fromm);
                    change(1,startt+1,startt+lenn - 1,stepp);
                    if(startt+lenn<=sizee&&startt+lenn>=1)
                    {
                        change(1,startt+lenn,startt+lenn,0-(stepp*(lenn - 1)));
                    }
                }
                else
                {
                    change1(1,startt-lenn+1,startt,fromm+(lenn - 1)*stepp);
                    change(1,startt-lenn+2,startt,0-stepp);
                    if(startt+1<=sizee&&startt+1>=1)
                    {
                        change(1,startt+1,startt+1,(stepp*(lenn - 1)));
                    }
                }
            }
            else
            {
                long long r;
                scanf("%lld",&r);
                long long ans = 0;
                ans += ask(1,1,r);
                ans += ask1(1,r,r);
                printf("%lld\n",ans);
            }
        }
        //cout<<"!!";
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值