士兵杀敌(四)
-
描述
-
南将军麾下有百万精兵,现已知共有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;
}
-
南将军麾下有百万精兵,现已知共有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;
}