2021国庆牛客第6场--2018牛客多校第6场 I-Team Rocket

题目传送门:click here

题意:
给定 n n n 个区间 [ l i , r i ] [l_i, r_i] [li,ri] ,每次给定一个点 x x x ,所有覆盖它的区间全部删除掉。求问,每次加入该点删掉多少个区间,每个区间在第几个点被删掉。(强制在线)

思考:
这道题在考场上我是没有想明白该怎么做,不过这道题和我前面出过的一道题很像,满足每个区间只会被删除一次的条件。
考虑一下怎么维护这道题的条件。

将区间左端点进行离散化,在区间左端点加入一个 pair(右端点,id),这个可以用个vector来存。可以在线段树中维护这个信息,线段树节点保存左端点在区间 [ l , r ] [l, r] [l,r] 的区间的 pair 信息,这样每个点就最多被存 l o g 2 n log_2{n} log2n 次。
初始的时候进行统一建树,在叶子按照右端点大小节点排序,然后利用归并的方式按照右端点大小合并。归并的复杂度是线性的。
在查询 x x x 删除多少个区间的时候,直接查询区间 [ 1 , l x ] [1, l_x] [1,lx] ,其中 l x l_x lx 是小于等于 x x x 的离散化的点。对每个区间 [ l , r ] [l,r] [l,r] 按照右端点的从大到小删除,然后标记已经删除的点不再进行删除。这样每个点只会被删除至多一次,总的复杂是 O ( n ∗ l o g 2 n ) O(n*log_2{n}) O(nlog2n) (线段树和删除操作不是相乘关系?)。

还有个问题就是,这道题不离散化的话会被卡空间。队友最开始没有离散化用 vector 开的 maxn * 64,即使没有添加任何元素进去,空间还是爆了。不太清楚 vector 的空间机制。最后换成链表的方式才跑过去。

代码的话就用队友 xgcxgc 的那份:

#include <bits/stdc++.h>
#define fo(i, a, b) for(int i = a; i <= b; i++)
#define LL long long
typedef unsigned int ui;
typedef unsigned long long ull;
using namespace std;
const int N = 2e5 + 1;
const int mod = 998244353;
const int M = 1e9;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*f;
}

struct node {int l, r;} a[N];
int tt[N], sz, lastans, nw;
bool dd[N]; int bb, Q[N * 64], nxt[N * 64], fir[N * 64];
struct tnode {
	int cnt, id, lc[N * 64], rc[N * 64];
	
	void get(int u, int l, int r, int p) {
		if(!u) return ;
		int z = fir[u];
		while(z) {
			int x = Q[z];
			if(!dd[x]) sz++, tt[x] = nw, lastans = (LL)lastans * x % mod;
			dd[x] = 1;
			z = nxt[z];
		} fir[u] = 0;
		if(l == r) return ;
		int mid = l + r >> 1;
		if(p <= mid) get(lc[u], l, mid, p);
		else get(rc[u], mid + 1, r, p);
	}
	
	void cg(int &u, int l, int r, int ll, int rr, int v) {
		if(!u) u = ++cnt, lc[u] = rc[u] = fir[u] = 0;
		if(l == ll && r == rr) {
			if(!fir[u]) fir[u] = ++bb, Q[bb] = v;
			else nxt[++bb] = fir[u], fir[u] = bb, Q[bb] = v;
			return ;
		} int mid = l + r >> 1;
		if(rr <= mid) cg(lc[u], l, mid, ll, rr, v);
		else if(ll > mid) cg(rc[u], mid + 1, r, ll, rr, v);
		else cg(lc[u], l, mid, ll, mid, v), cg(rc[u], mid + 1, r, mid + 1, rr, v);
	}
} t;

int main() {
	int T=read();
	fo(ti, 1, T)
	{
		printf("Case #%d:\n", ti);
		int n=read(),m=read();
		for(int i=1;i<=n;i++) a[i].l = read(), a[i].r = read(), tt[i] = dd[i] = 0;
		int rt = 0; t.cnt = t.id = 0; bb = 0;
		fo(i, 1, n) t.cg(rt, -M, M, a[i].l, a[i].r, i);
		lastans = 0;
		for(int i=1;i<=m;i++){
			int x = read() ^ lastans;
			if(x < -M || x > M) {lastans = 0, puts("0"); continue;}
			sz = 0; lastans = 1; nw = i;
			t.get(1, -M, M, x);
			if(sz == 0) lastans = 0;
			printf("%d\n", sz);
		} fo(i, 1, n) printf("%d ", tt[i]); puts("");
		fo(i, 1, bb) nxt[i] = Q[i] = 0;
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值