牛客网暑期ACM多校训练营(第六场)I Team Rocket (线段树)

题目链接:https://www.nowcoder.com/acm/contest/144/I

时间限制:C/C++ 4秒,其他语言8秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述 

There are n trains running between Kanto and Johto region. Assuming the railway is a number line, the i-th train travels from coordinate li to coordinate ri (both inclusive).

One day, m Team Rocket members invaded the railway system successfully. The i-th Team Rocket member was going to destroy the transportation hub with coordinate xi. Once a transportation hub within the driving range of a train is destroyed, the train's itinerary will be canceled immediately.

Giovanni wants to know how many train trips will be firstly canceled after each attack.

After all the attacks finished, for each train Giovanni needs to know that in which attack its itinerary was firstly canceled, or it was unaffected at all.

输入描述:

The input starts with one line containing exactly one integer T, which is the number of test cases.

For each test case, the first line contains two integers n and m, indicating the number of trains and the number of Team Rocket members.

Each of the next n lines contains 2 integers li and ri, indicating the driving range of the i-th train.

Each of the next m lines contains exactly one integer yi. 
, where xi is the transportation hub that Team Rocket members would destroy in the i-th attack, resi-1 is the product of the indexes of trips cancelled by the (i-1)-th attack and   means exclusive or. 

If no such trip exists, resi-1 is considered to be 0.

- 1 ≤ T ≤ 5.
- 1 ≤ n,m ≤ 2 x 105.
- -109 ≤ li ≤ ri ≤ 109.
- -109 ≤ xi ≤ 109.

输出描述:

For each test case, output one line "Case #x:" first, where x is the test case number (starting from 1).

Then output m lines, each line of which contains exactly one integer, indicating the number of train trips firstly canceled after the i-th attack.

Finally output one line, containing n integers, where the i-th integer is the time when the i-th train trip is firstly canceled or 0 if it is not affected.
示例1

输入

复制
1
3 4
1 3
2 6
-999 1000000000
-1000
1
5
2

输出

复制
Case #1:
0
2
1
0
2 3 2

题意:现在有n列火车,每列火车有自己的运营区间[L,R],现在有m个黑客,每个黑客攻击一个位置Xi,问每次攻击之后有几个火车停止运营,以及每列火车是在第几次攻击之后停止运营的,如果列车线路之间
包括攻击点Xi,那么该火车就停止运营。还有就是下一次攻击的节点是由上一次攻击结束之后得到的,也就是防止离线操作的。
思路:首先将该题简化,题意也就是说有n个区间[L,R],每次找到一个点,将包含该点的区间全部删除,找到每次操作之后有多少个区间收到影响,还需要记录一下每个区间在第几次操作之后被删除的。
线段树。首先我们对所有区间按照左端点升序排序,建立线段树,每个节点记录向右能够到达的最右边的地方,于是在我们进行查询的时候,如果这个节点能够到达的最右边也到达不了我们破坏的点,那么这个节
点以及它的子树对答案没有影响,我们在线段树上操作之前还可以二分找到左端大于破坏点的邻接区间的编号,这行的话,编号大于这个值的区间也不会被破坏,于是在线段树上判断每个区间与破坏点之间的关系
即可。每次操作的复杂度略大于log(n),于是m次操作的时间复杂度应该是O(m*log(n))带常数。
代码如下:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>

using namespace std;

const long long MAXN = 200005;
const long long mod = 998244353;
const long long inf = 100000000009;

long long t , n ,  m;
long long ans[MAXN];   ///记录的是每个区间在第几次之后被毁

struct INTERVAL
{
    long long l , r , index;
    INTERVAL(){}
    INTERVAL(long long _l , long long _r , long long _index)
    {
        l = _l;
        r = _r;
        index = _index;
    }
    bool operator < (const INTERVAL & b) const
    {
        return l < b.l;
    }
}interval[MAXN];

struct NODE
{
    long long r , index;   ///每个节点记录这个节点向右最远能到达的点
    NODE(){}
    NODE(long long _r , long long _index)
    {
        r = _r;
        index = _index;
    }
}node[MAXN*4];

void input()
{
    for(long long i=1; i<=n; i++)
    {
        scanf("%lld%lld" , &interval[i].l , &interval[i].r);
        interval[i].index = i;
    }
}

void update(long long id)
{
    node[id].r = max(node[id*2].r , node[id*2+1].r);
}

void build(long long root , long long L , long long R)
{
    if(L > R) return ;
    if(L == R)
    {
        node[root].r = interval[L].r;     ///id ? l ?
        node[root].index = interval[L].index;
        return ;
    }
    long long mid = (L+R)>>1;
    build(root*2 , L , mid);
    build(root*2+1 , mid+1 , R);
    update(root);
}

long long cnt;  ///以及本次攻击毁坏的铁路的数量
long long res;   ///记录上一步的结果  注意 不是破坏了多少个点 而是破坏的铁路的编号的乘积

void query(long long root , long long L , long long R , long long pos , long long x , long long num)  ///pos是二分到的值
{                                                        ///x是被毁坏的点 num是这是第几个黑客
        if(L>R || x>node[root].r || pos<L)
            return ;   ///如果爆破点在这个点最右边还要靠右或者二分到的点的右边的话 就炸不到 就跳过
        if(L == R)
        {
            ///这个点被攻击了
            cnt++;
            node[root].r = -inf;
            res = (res*node[root].index)%mod;
            ans[node[root].index] = num;
            return ;
        }
        long long mid = (L+R)>>1;
        query(root*2 , L , mid , pos , x , num);
        if(pos >= mid+1)   ///如果这个点右边还有区间是可能被破坏的
            query(root*2+1 , mid+1 , R , pos , x , num);
        update(root);
}

void debug()
{
    for(long long i=1; i<=7; i++)
    {
        printf("%lld..%lld..%lld..\n" , i , node[i].r , node[i].index);
    }
}

void debug2()
{
    for(long long i=1; i<=n; i++)
    {
        printf("%lld..%lld..%lld..%lld..\n" , i , interval[i].l , interval[i].r , interval[i].index);
    }
}

int main()
{
    scanf("%lld" , &t);
    for(long long cas=1; cas<=t; cas++)
    {
        printf("Case #%lld:\n" , cas);
        memset(ans , 0 , sizeof(ans));
        scanf("%lld%lld" , &n , &m);
        input();
        sort(interval+1 , interval+1+n);
//        debug2();
        build(1 , 1 , n);
//        debug();
        long long x;  ///下次攻击的位置  输入
        long long y;
        long long pos;   ///记录大于位置x的第一个区间的左端点,因为若左端点在位置x的
                   ///右边 那么这个区间一定没有被这个黑客破坏掉
        res = 0;   ///res 没有清空
        for(long long i=1; i<=m; i++)
        {
            scanf("%lld" , &y);
            x = y^res;
            cnt = 0;
            res = 1;
            pos = upper_bound(interval+1 , interval+1+n , INTERVAL(x,0,0))-interval-1;   ///upper_bound()里面三个参数的类型一定要一致
                                                                    ///按照左端点排序
            if(pos > 0)
                query(1 , 1 , n , pos , x , i);
            if(cnt == 0)
                res = 0;
            printf("%lld\n" , cnt);
        }
        for(long long i=1; i<=n; i++)
        {
            if( i!= 1)
                printf(" ");
            printf("%lld" , ans[i]);
        }
        printf("\n");
    }

    return 0;
}

 

posted @ 2018-09-12 21:46  Flower_Z  阅读(180)  评论(0编辑  收藏  举报