hdu 4031 树状数组 好题

题意:

       一排墙,如果它处于准备状态被攻击了,那么他就被成功攻击。即,若在k时间遭受攻击,则它在k + 1 ~ k + t - 1的时间里面都在准备状态,这时候攻击就是成功攻击。

       告诉你怎么攻击的,询问一个点被成功攻击了几次。

解:

       据说这题在比赛的时候三小时多只有一个人出。令我很费解的是,时间怎么和空间联系起来呢?

       听韦广讲述了一遍,我敲敲看。用树状数组记录空间,时间手动控制(其实是用一个结构体的下标表示时间)。答案 = 每个块被攻击次数(含成功和不成功的被攻击) - 成功防御次数,又是逆向思维

      T了。因为成功防御次数随着时间的增加只增不减,所以可以弄两个数组记录每个位置可以成功防御的时间和已经成功防御的次数。然后就A了。

      线段树和树状数组都行,拿来统计每个块被攻击的次数。

感触:

      这题真是神啊。但是怎么会这么多人都没做出来呢?做过两道时间和空间结合的题目,都和树状数组和线段树有关。那题是搞个数组记录该回去的时间。http://blog.csdn.net/julyana_lin/article/details/7846451

/*
Pro: 0

Sol:

date:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <set>
#include <vector>
#define maxn  20010
using namespace std;
int T,n,Q,t,a,b,sub,c[maxn],def_time[maxn],def_num[maxn];
struct Attack{  int s, e;  }att[maxn];
char op[10];
void modify(int pos, int val){
    while(pos <= n){
        c[pos] += val;
        pos += (pos & -pos);
    }
}
int getsum(int pos){
    int sum = 0;
    while(pos){
        sum += c[pos];
        pos -= (pos & -pos);
    }return sum;
}
int main(){
    scanf("%d",& T);
    for(int ca = 1; ca <= T; ca ++){
        printf("Case %d:\n",ca);
        memset(c,0,sizeof(c));  sub = 0;
        memset(def_num,0,sizeof(def_num));
        memset(def_time,0,sizeof(def_time));
        scanf("%d%d%d",&n,&Q, & t);
        for(int j = 0; j < Q; j ++){
            scanf("%s",op);
            if(op[0] == 'A'){
                scanf("%d%d",&att[sub].s,&att[sub].e);
                modify(att[sub].s,1);
                modify(att[sub].e + 1,-1);  sub ++;
            }else{
                int pos;
                scanf("%d",&pos);
                for(int i = def_time[pos]; i < sub; i ++){
                    if(att[i].s <= pos && pos <= att[i].e){
                        def_num[pos] ++;
                        def_time[pos] = i + t;
                        i += (t - 1);
                    }
                }
//                int prev;
//                for(int i = 0; i < sub; i ++)
//                    if(att[i].s <= a && att[i].e >= a) { prev = i; sum --; break;}
//                for(int i = prev + 1; i < sub; i ++){
//                    while( (att[i].s > a || att[i].e < a) && i < sub){i ++; }
//    -                if(i >= sub) break;
//                    if(i - prev >= t) {sum --; prev = i;}
//                }
                printf("%d\n",getsum(pos) - def_num[pos]);
            }
        }
    }
	return 0;
}
贴线段树的代码:


#include<cstdio>
#include<cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn = 20001;
int sum[maxn<<2];
int col[maxn<<2];
struct pp{
    int l,r;
}att[maxn];
int defen[maxn],pos[maxn];
void update(int L,int R,int l,int r,int rt){
    if(L<=l&&r<=R){//完全覆盖就不再更新了,否则就退化成O(n),必然导致超时
           sum[rt]++;
              return ;
    }
    int m=(l+r)>>1;
    if(L<=m) update(L,R,lson);
    if(R>m) update(L,R,rson);
}
int query(int p,int l,int r,int rt){//依次把树上的覆盖次数相加,加到底的时候就是某个点的覆盖次数了
    if(l==r){
        return sum[rt];
    }
    int m=(l+r)>>1;
    int ret=0;
    ret+=sum[rt];
    if(p<=m) return ret+query(p,lson);
    else return ret+query(p,rson);
}
int main(){
    int t,cases=1,i,j,t0,a,b,n,q;
    char s[10];
    scanf("%d",&t);
    while(t--){
        int tot=0;
        memset(pos,0,sizeof(pos));
        memset(defen,0,sizeof(defen));
        scanf("%d%d%d",&n,&q,&t0);att[0].l=att[0].r=0;
        memset(rt,0,sizeof(rt));
        printf("Case %d:\n",cases++);
        while(q--){
            scanf("%s",s);
            if(s[0]=='A'){
                scanf("%d%d",&a,&b);
                tot++;
                att[tot].l=a;att[tot].r=b;
                update(a,b,1,n,1);
            }
            else {
                scanf("%d",&a);
                for(i=pos[a];i<=tot;i++){
                    if(a>=att[i].l&&a<=att[i].r){
                        pos[a]=i+t0;
                        defen[a]++;
                        i+=t0-1;
                    }
                }
                printf("%d\n",query(a,1,n,1)-defen[a]);
            }
        }
    }
    return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值