士兵杀敌(4,5)(区间修改、查询)

士兵杀敌(四)

时间限制: 2000 ms  |  内存限制: 65535 KB
难度: 5
描述

南将军麾下有百万精兵,现已知共有M个士兵,编号为1~M,每次有任务的时候,总会有一批编号连在一起人请战(编号相近的人经常在一块,相互之间比较熟悉),最终他们获得的军功,也将会平分到每个人身上,这样,有时候,计算他们中的哪一个人到底有多少军功就是一个比较困难的事情,军师小工的任务就是在南将军询问他某个人的军功的时候,快速的报出此人的军功,请你编写一个程序来帮助小工吧。

假设起始时所有人的军功都是0.

输入
只有一组测试数据。
每一行是两个整数T和M表示共有T条指令,M个士兵。(1<=T,M<=1000000)
随后的T行,每行是一个指令。
指令分为两种:
一种形如
ADD 100 500 55 表示,第100个人到第500个人请战,最终每人平均获得了55军功,每次每人获得的军功数不会超过100,不会低于-100。
第二种形如:
QUERY 300 表示南将军在询问第300个人的军功是多少。
输出
对于每次查询输出此人的军功,每个查询的输出占一行。
样例输入
4 10
ADD 1 3 10
QUERY 3
ADD 2 6 50
QUERY 3
样例输出
10
60


因为只是单点查询,所以用线段树记录各个区间的修改情况就可以了,不用求个区间的和。查询的时候会经过所以包含这个点的区间,然后把这些区间的修改都累加起来就是单个点的最终修改情况。

 
#include <stdio.h>
#include <string.h>
#include <stack>
#include <math.h>

#define LL long long int
using namespace std;

struct stree
{
    int l,r;
    int v;
} tree[4000005];


void create(int l, int r, int now)
{
    tree[now].l = l;
    tree[now].r = r;
    tree[now].v = 0;
    if(l == r)
        return;
    int m = (l + r)/2;
    create(l,m,now*2);
    create(m+1,r,now*2+1);
    return ;
}

//void pushdown(int now, int m)
//{
//    if(tree[now].lazy != 0)
//    {
//        tree[now<<1].v += tree[now].lazy*(m - tree[now].l + 1);
//        tree[now<<1].lazy += tree[now].lazy;
//        tree[now<<1|1].v += tree[now].lazy*(tree[now].r - m);
//        tree[now<<1|1].lazy += tree[now].lazy;
//        tree[now].lazy = 0;
//    }
//}

int query(int l, int r, int now)
{
    if(l == tree[now].l && r == tree[now].r)
    {
        //printf("%d\n", tree[now].v);
        return tree[now].v;
    }
    int m = (tree[now].l + tree[now].r) / 2;
    //pushdown(now, m);
    if(r <= m)
        return query(l, r, now*2) + tree[now].v;
    else if(l > m)
        return query(l, r, now*2+1) + tree[now].v;
    else
    {
        return query(l, m, now*2) + query(m+1, r, now*2+1) + tree[now].v;
    }
}

void add(int l, int r, int now, int v)
{
    if(l == tree[now].l && r == tree[now].r)
    {
        //tree[now].lazy += v;
        tree[now].v += v;
        return;
    }
    int m = (tree[now].l + tree[now].r) / 2;
    //lazydown(l,r,m,now);
    if(r <= m)
        add(l, r, now*2, v);
    else if(l > m)
        add(l, r, now*2+1, v);
    else
    {
        add(l, m, now*2, v);
        add(m+1, r, now*2+1, v);
    }
    return;
}

int main()
{
    int N,M,a,b,c;
    char s[7];
    scanf("%d %d", &M, &N);
//    for(int i=1;i<=N;++i)
//    {
//        scanf("%d",&z[i]);
//    }
    create(1,N,1);
    for(int i=0;i<M;++i)
    {
        scanf("%s", s);
        if(s[0] == 'Q')
        {
            scanf("%d", &a);
            printf("%d\n", query(a, a, 1));
        }
        else if(s[0] == 'A')
        {
            scanf("%d%d%d", &a, &b, &c);
            add(a, b, 1, c);
        }
    }
    return 0;
}



        


士兵杀敌(五)

时间限制: 2000 ms  |  内存限制: 65535 KB
难度: 5
描述

南将军麾下有百万精兵,现已知共有M个士兵,编号为0~M,每次有任务的时候,总会有一批编号连在一起人请战(编号相近的人经常在一块,相互之间比较熟悉),最终他们获得的军功,也将会平分到每个人身上,这样,有时候,计算他们中的哪一个人到底有多少军功就是一个比较困难的事情。

在这样的情况下,南将军却经常会在许多次战役之后询问军师小工第i号士兵到第j号士兵所有人的总军功数。

请你帮助军师小工回答南将军的提问。

输入
只有一组测试数据
第一行是三个整数N,C,Q(1<=N,C,Q<=1000000),其中N表示士兵的总数。
随后的C行,每行有三个整数Mi,Ni,Ai(0<=Mi<=Ni<=N,0<=Ai<=100),表示从第Mi号到第Ni号士兵所有人平均增加了Ai的军功。
再之后的Q行,每行有两个正正数m,n,表示南将军询问的是第m号士兵到第n号士兵。
输出
请对每次询问输出m号士兵到第n号士兵的总军功数,由于该数值可能太大,请把结果对10003取余后输出
样例输入
5 3 2
1 3 2
2 4 1
5 5 10
1 5
2 3
样例输出
19
6



与上一题相比把单点查询改成了区间查询。写了个维护区间的和的选段树超时了,可能写太烂了。后来发现有个巧妙的方法,因为修改和查询是分开进行的,所以可以直接用离线算法。先记录修改的情况。比如样例中的1到3加2。那就把数组中sum[1] += 2   sum[3+1] -= 2这样,最后累加起来就相当于把sum数组里面1到3都加了2,而3后面的数则没有影响(这题有BUG,编号是可以从0开始,我这里的处理是都加1)

#include <stdio.h>
#include <string.h>
#include <stack>
#include <math.h>
#include <vector>

#define LL long long int
#define MOD 10003
using namespace std;

int sum[1000005];

int main()
{
    int N,C,Q,a,b,c;
    scanf("%d%d%d", &N, &C, &Q);
    ++N;
    while(C--)
    {
        scanf("%d%d%d", &a, &b, &c);
        ++a;
        ++b;
        sum[a] += c;
        sum[b+1] -= c;
    }
    for(int i=1;i<=N;++i)//把所有修改累加起来得到最后的值
    {
        sum[i] += sum[i-1];
    }
    for(int i=1;i<=N;++i)//这里累加是得到前缀和,方便计算区间和
    {
        sum[i] = (sum[i] + sum[i-1])%MOD;
    }
    while(Q--)
    {
        scanf("%d%d", &a, &b);
        ++a;
        ++b;
        printf("%d\n", (sum[b] + MOD - sum[a-1])%MOD);
    }
    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值