luogu2161
省选 / NOI-
题面:
设计一个数据结构,支持两种操作:
1. A i j
添加一个新的预约[i, j],并删除所有与其冲突的预约。返回此次操作删除的预约的个数。
2. B
返回当前的预约总数。
数据范围:操作数<=200000
思路:
线段树+染色
1、颜色。每个预约均用一种颜色代表
struct tColor
{
int left, right, num;
//表示区间[left, righe] 为num色
} color[MAXN];
2、线段树。包含添加颜色功能(包含了删除颜色), 其中删除颜色中又包含了单点修改(为了修改sumv值)
struct Segment_Tree
{
int colorv[MAXN*4], coloropt[MAXN*4];
//colorv:当前区间的颜色(-1:无色, -2:杂色, 1~n:某种颜色);
//coloropt:改变颜色的懒惰标记, 默认为0
#define lson (o<<1)
#define rson (o<<1|1)
常规的pushup, pushdown, build, change函数:
inline void pushup(int o)
{
if(colorv[lson] == colorv[rson]) colorv[o] = colorv[lson];
else colorv[o] = -2; //标记为杂色
return ;
} //向上推颜色
inline void pushdown(int o)
{
if(coloropt[o] == 0) return ;
colorv[lson] = coloropt[o];
colorv[rson] = coloropt[o];
//将颜色向下推
coloropt[lson] = coloropt[o];
coloropt[rson] = coloropt[o];
//将标记向下推
coloropt[o] = 0;
//初始化标记
return ;
}
inline void build(int o, l, r) //建树, 在此题中仅作初始化用
memset(colorv, -1, sizeof(colorv)); //-1:无色
memset(coloropt, 0, sizeof(coloropt)); //0:无标记
memset(sumv, 0, sizeof(sumv));
return ;
inline void change(int o, int l, int r, int k, int v) //单点修改, 和其他线段树的单点修改并无太大差异, 但仅修改sumv值; k:修改的点的位置, v:修改后的值
{ ... }
删除颜色:
inline void deletecolor(int o, int l, int r, int ql, int qr) //删除一种颜色, ql=color[n].left, qr=color[n].right
{
if(l>=ql && r<=qr)
{
colorv[o] = -1;
coloropt[o] = -1;
return ;
} //将color范围内的颜色全部删除, 因为不存在一个区间有多种颜色的情况, 所以不用考虑杂色的问题
pushdown(o, l, r);
int mid = (l+r) >>1;
if(mid>=ql) deletecolor(lson, l, mid, ql, qr);
if(mid+1<=qr) deletecolor(rson, mid+1, r, ql, qr);
pushup(o);
return ;
}
添加颜色
每一次添加颜色时,都要搜索到该颜色所在的区间,所以在删除颜色时,删除区间可能比添加的区间要大,导致删除的标记被打在染色标记上面。这时pushdown便会把 now_color覆盖所以每次删除后,都直接退出至主函数(利用done), 然后再重头搜索一遍:
inline int addcolor(int o, int l, int r, int ql, int qr) //添加颜色
{
if(l>=ql && r<=qr && colorv[o] != -2) //如果当前节点有多个颜色, 则继续向下搜索(-2代表杂色)
{
if(colorv[o] == -1) { colorv[o] = now_color; coloropt[o] = now_color; return 0; } //如果该节点无色, 则直接打上标记
if(colorv[o] == now_color) return 0; //如果该节点与将要修改的节点的颜色相同, 则直接跳过
available --; //available:当前有效的颜色数
int tmpl = color[tmpcolor].left, tmpr = color[tmpcolor].right;
deletecolor(1, 1, TREE_WIDTH, tmpl, tmpr);
done = true;
return 1;
}
pushdown(o, l, r);
int mid = (l+r) >>1, ans = 0;
if(mid>=ql) ans += addcolor(lson, l, mid, ql, qr);
if(done == true) return ans ;
if(mid+1<=qr) ans += addcolor(rson, mid+1, r, ql, qr);
pushup(o);
return ans;
}
} st;
int main()
{
...
do {
done = false;
tmpans += st.addcolor(1, 1, TREE_WIDTH, l, r);
} while(donw == true);
printf("%d\n", tmpans);
...
}
此做法较为暴力,luogu #10跑了800+ms, 数据如果再强一点应该就会TLE。因为每次删除完一个点之后就会重新从头开始跑一次,可以在这里着手优化:若第一次搜索到一个颜色并满足下面的条件,则直接删除,继续pushdown。
((l>=ql && l<=qr) || (r>=ql && r<=qr)) && colorv[o]!=-1 && colorv[o]!=-2 && colorv[o]!=now_color