题目链接:[HDU 4031]Attack[树状数组区间更新单点查询]
题意分析:
『基地』组织又要打美国啦,如今他使用一种武器,可以攻打一个范围内的目标。但是米国有护盾,每个连续段内都有护盾,可以抵挡一次攻击。但是护盾有CD,
一次之后要过t时间才能回复,然而恐怖分子的没有CD,果然反派都是流弊XD。给出多个攻击和查询,每次查询输出该点被成功攻击了几次。
解题思路:
成功攻击次数怎么计算呢?很好的表示方法是:被攻击次数 - 成功抵御次数。那么问题来了,怎么计算抵御次数?可以另设一个数组pos记录当前查询时,x位置第一次抵御住攻击的次数。然后从这次开始往后查询到最新,如果有被攻击,那么就成功次数++并且更新一下成功抵御的位置。
而delta数组记录的是[x,n]这个区间的共同增量,所以segma(delta[i]) (0 < i <= x) 就是x这个点被攻击的次数。
个人感受:
终于是领略了三种模型了。点更+段查;段更+点查;段更+段查。这题理论上最坏情况能有1e8吧,不过不管啦,就酱紫XD。
具体代码如下:
#include<iostream> #include<cstring> #include<cstdio> #define lowbit(x) (x & (-x)) using namespace std; const int MAXN = 2e4 + 111; int n, q, t, delta[MAXN], pos[MAXN], defence[MAXN]; struct interval{ int l, r; }a[MAXN]; void add(int x, int val) { while (x <= n) { delta[x] += val; x += lowbit(x); } } int getSum(int x) { int ret = 0; while (x > 0) { ret += delta[x]; x -= lowbit(x); } return ret; } int main() { int up; char op[10]; scanf("%d", &up); for (int kase = 1; kase <= up; ++kase) { printf("Case %d:\n", kase); scanf("%d%d%d", &n, &q, &t); memset(delta, 0, sizeof delta); memset(pos, 0, sizeof pos); memset(defence, 0, sizeof defence); int cnt = 0, l, r, x; while (q --) { scanf("%s", op); if (op[0] == 'A') { scanf("%d%d", &l, &r); a[cnt].l = l, a[cnt].r = r; ++cnt; add(l, 1); add(r + 1, -1); } else { scanf("%d", &x); for (int i = pos[x]; i < cnt; ++i) { if (a[i].l <= x && x <= a[i].r) { pos[x] = i + t; ++defence[x]; i += t - 1; } } printf("%d\n", getSum(x) - defence[x]); } } } return 0; }