题目: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;
}