牛客网暑期ACM多校训练营(第六场)I-Team Rocket(树状数组处理区间最大值)

题目链接

题目:n个区间[l,r],m个操作:给数b,删去包含点b^res的区间,记录每个区间i第几次操作时被删去,每次操作删去的区间数。
res是上次删除的区间编号的乘积%998244353,如果上次未删区间,即res为0。


遇到题目,想了好久才整理出题目本质!
首先我们按左端点从l小到大的顺序排序,实际上是个离散化过程!
我们要删除包含x的区间,即等价于我们先找到满足p[R].l<=x的最大R值,
我们不要急于找出所有[1,id]区间中右端点大与等于x的区间!
我们可以找出一个最大的r的区间编号id(排序离散l后的编号),可想而知,如果它小 于x,即没有可删除的区间! 如果大于x,我们需要删除这个区间,我们把p[id].r 修改为无穷小的数,它以后永远无法比操作的x大,等价于删除了!然后找下去,直到没有可删为止!


我们的问题变为: 处理区间[1,id]的最大值的下标查询
单点修改(修改为无穷小)
用树状数组维护: 树状数组构建:O(nlogn)
最多n次修改(删去n个区间):O(nlogn*logn)
区间查询最多次数(n+m)(第1次查删完):O((n+m)logn)
所以时间复杂度O((n+m)logn+nlogn*logn).


笔者太菜了! 树状数组的最大值维护早忘记了!编码错误百出,特在此处总结写法!
1、得到数组,构建树状数组

/*****
*** 1、将所有c[i]初始化为对应的i
*** 2、i从第1至n的顺序,将j=i……j+=j&(-j),所有的c[j]更新
***    实际上我认为i&1的时候,才需要向上更新
******/
void mkStree(){
    for(int i=1;i<=n;++i)
        c[i] = i;
    for(int i=1;i<=n;++i)
        for(int j=i;j<=n;j+=j&(-j))//我码代码时j=1开始,等于干了n遍一模一样!
            if(p[c[j]].r<p[c[i]].r) c[j] = c[i];
}

2、单点修改
这里有个规律:c[x]是记录的a[x]、c[x-1]、c[x-2]、c[x-4]……c[x-(lowbit(x)>>1)的最大值!这样是不是很简单了!(mmp:我模仿c[8] = a[8] + c[7] + c[6] + c[4] ,归了错误结论:更新x点,即拿a[x]+c[x-1]+c[low(x-1)]+c[low(low(x-1))]……实际上拿x=7模拟下,就是错的!)

void modify(int pos){
    /*修改c[pos]及包括出pos位的值*/
    for(int x=pos;x<=n;x+=x&(-x)){
        c[x] = x;
        for(int y=1;y<(x&(-x));y<<=1)
            if(p[c[x]].r<p[c[x-y]].r) c[x] = c[x-y];
    }

}

3、区间查询([1,R])
本题因为确定是求[1,R]里的最大值,可模仿树状数组求和简单得到!
时间复杂度为O(logn)

/*查询[1,x]上的最大值*/
int query(int x){
    int id = 0;
    for(;x>0;x-=x&(-x))
        if(p[id].r<p[c[x]].r) id = c[x];
    return id;
}

4、区间查询([l,r])
当lowbit(y)超过了查询区间长度,我们可以单独用p[y].r判,y降1,那么c[y-1]、c[y-2]、都能用了!

int query(int x,int y){
    int id = 0; 
    while(y>=x){
        if(p[y].r>p[id].r) id = y;
        for(y--;y-(y&(-y))>=x;y-=y&(-y))
            if(p[c[y]].r>p[id].r) id = c[y];
    }
    return id;
}
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <set>
#include <map>
#include <vector>
#define llt long long
using namespace std;
/******
   有n个区间,m次操作: 给数b问删去包含x=b^(resi-1%mod)的区间数,和区间什么时候删除的。
   resi-1记录的是上一次删除点的标号的乘积。

*/

//upper_bound() 查询 第一个大于 num的位置,没有就输出end()

const int N = 2e5+777;
const int INF = 1e9+7777;
const llt mod = 998244353;
int A[N],Cnt[N];//A[i]记录:原输入第i个区间线段,第几次删去
                //Cnt[i]记录: 第i次删去多少个区间线段
struct node{
    int l,r;
    int id;//保存以前的编号
}p[N];

int c[N];
int n,m;

void mkStree(){
    for(int i=1;i<=n;++i)
        c[i] = i;
    for(int i=1;i<=n;++i)
        for(int j=i;j<=n;j+=j&(-j))
            if(p[c[j]].r<p[c[i]].r) c[j] = c[i];
}
void modify(int pos){
    /*修改c[pos]及包括出pos位的值*/
    for(int x=pos;x<=n;x+=x&(-x)){
        c[x] = x;
        for(int y=1;y<(x&(-x));y<<=1)
            if(p[c[x]].r<p[c[x-y]].r) c[x] = c[x-y];
    }

}


int query(int x,int y){
    int id = 0; 
    while(y>=x){
        if(p[y].r>p[id].r) id = y;
        for(y--;y-(y&(-y))>=x;y-=y&(-y))
            if(p[c[y]].r>p[id].r) id = c[y];
    }
    return id;
}

/*查询[1,x]上的最大值*//*
int query(int x){
    int id = 0;
    for(;x>0;x-=x&(-x))
        if(p[id].r<p[c[x]].r) id = c[x];
    return id;
}*/
/*节点按l排序,离散化l,而r即为数组的值*/
bool cmp(const node  &a,const node  &b){return a.l<b.l||(a.l==b.l&&a.r<b.r);}


void init(){
     memset(A,0,sizeof(A));
     memset(Cnt,0,sizeof(Cnt));

     scanf("%d%d",&n,&m);
     for(int i=1;i<=n;++i){
         scanf("%d%d",&p[i].l,&p[i].r);
         p[i].id = i;
     }
     p[0].r = -INF;
     sort(p+1,p+1+n,cmp);//正式将l离散化为数组编号
//     for(int i=1;i<=n;++i){
//        printf("Point: %d %d\n",p[i].l,p[i].r);
//
//     }
     mkStree();
}
int main(){
    int cas;
    scanf("%d",&cas);
    for(int k=1;k<=cas;++k){
        init();
        node a;
        a.r = INF;
        int res = 0;
        for(int i=1;i<=m;++i){
            scanf("%d",&a.l);
            a.l ^=res;
            //cout<<i<<" "<<a.l<<endl;
            res = 1;
            if(p[1].l>a.l) {//删去的x位于所有区间的右侧
                //Cnt[i] = 0;
                res = 0;
            }else{
                int R = upper_bound(p+1,p+1+n,a,cmp)-p-1;
                /*查询[1,R]中r的最大值的下标id*/
                while(true){
                    int id = query(1,R);
                     //cout<<"i: "<<i<<" "<<id<<" "<<p[id].l<<" "<<p[id].r<<endl;
                    if(p[id].r<a.l) break;
                    /*线段p[id].id删去*/
                    res = 1LL*res*p[id].id%mod;
                    A[p[id].id] = i;
                    ++Cnt[i];
                    p[id].r = -INF;
                    modify(id);
                }
                if(Cnt[i]==0) res = 0;
            }

        }
        printf("Case #%d:\n",k);
        for(int i=1;i<=m;++i) printf("%d\n",Cnt[i]);
        for(int i=1;i<=n;++i) printf("%d%c",A[i]," \n"[i==n]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38786088/article/details/81432228
今日推荐