SMOJ 2202 怪物 (整体二分+BIT)

题目描述

这里写图片描述


输入格式

第一行为数据组数。
对于每一组数据:
这里写图片描述


输出格式

对于每一组数据:
共N行,第i行输出一个整数,表示第i个团队最早是在哪一轮攻击后就解散,或者-1.


输入样例

1
3 5
1 3 2 1 3
10 5 7
3
4 2 4
1 3 1
3 5 2


输出样例

3
-1
1


题解

这题是整体二分的果题,本来没有什么好讲的,但是蒟蒻我还是打算水一篇博客。。。(因为ACM我们队大半个上午都在码这题,还码不对这题还是有一些值得总结的地方的)

这题与动态区间第K大是有区别的,区别在于修改操作不用放进队列了。这是为什么呢?因为我们二分轮数就可以直接知道该进行哪一段操作了,所以没有放进队列里的必要了?这并不是本质的区别。因为我们动态区间第K大里也可以先将询问排序然后整体二分中再二分找到修改的上下界并完成修改。(很有道理)

你说等等?排序?发现问题了,由于我们要保证询问与修改的相对顺序,所以哪能排序啊!这才是本质的区别。本题题目并没有修改、查询的交替给出,所以并不需要将修改当做操作,放进队列中保证相对顺序地排序,而是直接根据二分出来的轮数进行攻击就可以了。然后套用BIT,其他的跟动态第K大的写法几乎一样。另外就是先用邻接链表记一下每个团队,将每个团队当做一个询问,每次攻击完就扫一遍链表,安置询问位置,这依旧是跟队列长度有关,所以不会退化。对于-1就多加一轮判一下就行了。

那假如修改不在询问之后,直接个体二分行不行呢?当然会超时,这是 O(nKlogm) 的,就是个暴力。

话说这数据有点水,没开long long也能过。。。


代码

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#define N 300010

using namespace std;

int nG, n, m, K;
int head_p[N], cur, BIT[N], ans[N], S[N];
int qL[N], qR[N], q[N];
bool Left[N];

struct Data{
    int x, y, C;
}attack[N];

struct Aadj{
    int next, obj;
}Edg[N];

void Insert(int a, int b){
    cur ++;
    Edg[cur].next = head_p[a];
    Edg[cur].obj = b;
    head_p[a] = cur;
}

int lowbit(int x){
    return x & (-x);
}

void Add(int x, int v){
    for(int i = x; i <= m; i += lowbit(i))  BIT[i] += v;
}

int Sum(int x){
    int res = 0;
    for(int i = x; i > 0; i -= lowbit(i))  res += BIT[i];
    return res;
}

void Binary(int L, int R, int he, int ta){
    if(he > ta)  return;
    if(L == R){
      for(int i = he; i <= ta; i++){
        int tmp = q[i];
        ans[tmp] = L;
      }
      return;
    }

    int mid = (L + R) >> 1;

    for(int i = L; i <= mid; i++){
      if(attack[i].x <= attack[i].y)  Add(attack[i].x, attack[i].C), Add(attack[i].y+1, -attack[i].C);
      else  Add(attack[i].x, attack[i].C), Add(1, attack[i].C), Add(attack[i].y+1, -attack[i].C);
    }

    for(int i = he; i <= ta; i++){
      int tmp = q[i], res = 0;
      for(int j = head_p[tmp]; ~ j; j = Edg[j].next){
        int v = Edg[j].obj;
        res += Sum(v);
      }
      if(res >= S[tmp])  Left[i] = true;
      else  S[tmp] -= res;
    }

    for(int i = L; i <= mid; i++){
      if(attack[i].x <= attack[i].y)  Add(attack[i].x, -attack[i].C), Add(attack[i].y+1, attack[i].C);
      else  Add(attack[i].x, -attack[i].C), Add(1, -attack[i].C), Add(attack[i].y+1, attack[i].C);
    }

    int cnt1 = 0, cnt2 = 0;
    for(int i = he; i <= ta; i++){
      int tmp = q[i];
      if(Left[i])  qL[++cnt1] = tmp;
      else  qR[++cnt2] = tmp;
      Left[i] = false;
    }

    for(int i = 1; i <= cnt1; i++)  q[he+i-1] = qL[i];
    for(int i = 1; i <= cnt2; i++)  q[he+cnt1+i-1] = qR[i];

    Binary(L, mid, he, he+cnt1-1);
    Binary(mid+1, R, he+cnt1, ta);
}

int main(){

    freopen("2202.in", "r", stdin);
    freopen("2202.out", "w", stdout);

    scanf("%d", &nG);
    while(nG --){
      scanf("%d%d", &n, &m);

      cur = -1;
      for(int i = 1; i <= n; i++)  head_p[i] = -1;

      int x;
      for(int i = 1; i <= m; i++)  scanf("%d", &x), Insert(x, i);

      for(int i = 1; i <= n; i++)  scanf("%d", &S[i]);

      scanf("%d", &K);
      for(int i = 1; i <= K; i++)  
        scanf("%d%d%d", &attack[i].x, &attack[i].y, &attack[i].C);

      for(int i = 1; i <= n; i++)  q[i] = i;
      Binary(1, K+1, 1, n);
      for(int i = 1; i <= n; i++){
        if(ans[i] == K+1)  ans[i] = -1;
        printf("%d\n", ans[i]);
      }
    }
    return 0;
}

还会有人让你睡不着
还能为某人燃烧

这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值