ZOJ3453 Doraemon's Sweet Bullet(线段树单点更新+区间更新+区间查询)

Doraemon's Sweet Bullet

Time Limit: 2 Seconds       Memory Limit: 65536 KB

Doraemon likes to shoot his enemies with sweet bullets.

n "enemies" stand in a line, each with a life value li (the index i increases from left to right side and starts from 1). Each time Doraemon shoots a sweet bullet from the right side. The ith bullet has a "critical range" ki. That is to say, it attacks the first enemy from right side whose life value is equal to or greater than ki. The life value of the attacked enemy will decrease to 1 immediately.

However, Doraemon finds a terrible fact that after an enemy is attacked, it will distribute the sweets to his friends and his friends' life value will increase by 1. For the ith enemy, his friends are in a consecutive range from the aith enemy to the bith. Note that an enemy may be the friend of himself so that after being attacked his life value will be 2.

What's more, if a bullet can't find a target--there doesn't exist an enemy with life greater or equal than ki, all enemies' life will increase by 1.

Now Doraemon wants to know after m bullets were shot, what the maximum life value in all enemies is.

Input

The input contains multiple test cases.

Each test case begins with a line containing a single integer n (1 ≤ n ≤ 100000) indicating the number of enemies.

The following n lines describes enemies, one enemy each line. Each line contains three integers, the initial life value li (1 ≤ li ≤ 10000) and his range of friends ai bi (1 ≤ ai ≤ bi≤ n) - that is, enemies with index from ai to bi (inclusive) are all his friends.

The following line contains an integer m (1 ≤ m ≤ 100000) indicating the number of bullets Doraemon has shot. The last m lines follows, each with a single integer ki (1 ≤ ki ≤ 10000) describing the bullet's "critical range".

Different cases are separated by a blank line.

Process to the end of input.

Output

For each test case, output the maximum life value after shooting in a single line.

Sample Input
3
3 1 2
4 1 3
5 1 1
3
4
2
1
Sample Output

6

题意:有N个敌人,排成一排编号为1~n;对于敌人i有一个初始value[i];对于敌人i,其朋友范围区间[Li,Ri],i可能在[Li,Ri]区间内。你每次从右边发射子弹,第i颗子弹值为Ki,打中第一个value值大于或等于Ki的敌人,该敌人value值变为1,其朋友范围内的敌人value值均增加1;但是,如果没有敌人的value值大于或者等于Ki,则所有敌人value值增加1。求最后敌人中最高的value值。

解法:线段树,每个节点设置一个max、add元素,max表示该区间上的最大值,add表示该区间增加的值;实现(1)区间段元素+1操作,即对应的区间add+1;(2)对于对某个value值置1,即可将该点覆盖的所有区间add累加值+1;(3)查找大于或等于K的最右元素。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <cmath>
#include <algorithm>
#include <cstdlib>
using namespace std;
#define maxn 100009

int val[maxn],l[maxn],r[maxn]; //val存原来的值 l、r保存区间端点
int n;

struct Tree
{
    int s,t;
    int max_val;
    int add; //其实相当于lazy标记
}tree[maxn*4]; //树的大小要开到原来的4倍

void build(int id,int s,int t)
{
    tree[id].s=s;
    tree[id].t=t;
    tree[id].add=0;
    if(s==t)
    {
        tree[id].max_val=val[s];
        return;
    }
    int mid=(tree[id].s+tree[id].t)>>1;
    build(id*2,s,mid);
    build(id*2+1,mid+1,t);
    tree[id].max_val=max(tree[id*2].max_val,tree[id*2+1].max_val);//存大的
}

void pushdown(int id)//延迟更新
{
    if(tree[id].add!=0)
    {
        tree[id*2].max_val+=tree[id].add;
        tree[id*2+1].max_val+=tree[id].add;
        tree[id*2].add+=tree[id].add;
        tree[id*2+1].add+=tree[id].add;
        tree[id].add=0;
    }
}

void update1(int id,int s) //单点更新(被子弹打到的点)
{
    if(tree[id].s==s && tree[id].t==s)
    {
        tree[id].max_val=1;
        return;
    }
    int mid=(tree[id].s+tree[id].t)>>1;
    if(mid<s)
        update1(id*2+1,s);
    else
        update1(id*2,s);
    tree[id].max_val=max(tree[id*2].max_val,tree[id*2+1].max_val);
}

void update2(int id,int s,int t,int val)//这里val的值其实是1
{
    if(tree[id].s==s && tree[id].t==t)
    {
        tree[id].add+=val;
        tree[id].max_val+=val;
        return;
    }
    int mid=(tree[id].s+tree[id].t)>>1;
    pushdown(id); //延迟更新操作
    if(s>mid)
        update2(id*2+1,s,t,val);
    else if(t<=mid)
        update2(id*2,s,t,val);
    else
    {
        update2(id*2,s,mid,val);
        update2(id*2+1,mid+1,t,val);
    }
    tree[id].max_val=max(tree[id*2].max_val,tree[id*2+1].max_val);
}

int query(int id,int s,int t,int aim) //从右边开始查找第一个大于等于aim的
{
    if(s==t)
        return s;
    int mid=(tree[id].s+tree[id].t)>>1;
    pushdown(id);
    if(tree[id*2+1].max_val>=aim)
        return query(id*2+1,mid+1,t,aim);
    else
        return query(id*2,s,mid,aim);
}

int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%d %d %d",&val[i],&l[i],&r[i]);
        }
        build(1,1,n);
        int m,tmp;
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&tmp);
            if(tmp>tree[1].max_val)
                update2(1,1,n,1);
            else
            {
                int p=query(1,1,n,tmp);
                update1(1,p);
                update2(1,l[p],r[p],1);
            }
        }
        printf("%d\n",tree[1].max_val);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值