[COCI 2023/2024 #2] Dizalo

传送门

无删除

先分析 \(q=0\) 这个部分分,以便下文的展开。实际上认真看完题目,我们可以发现处理完一个点后,只要让 \(i\) 前的点按 \(a_i\) 升序排列,就可使答案最小,显然成立,这里不做证明。

很容易发现,若一个数后面存在一个数小于该数,则这个数不会产生任何附加代价。因此需要计算附加代价的数应该是序列的后缀最小值。

至此,我们将问题转换为,求每个后缀最小值 \(a_i\) 之前有多少个数大于 \(a_i\)。同时,注意题目里的条件,\(a\) 数组为一个全排列。又由于 \(a_i\) 为后缀最小值,那么就可以知道,序列一共有 \(n-a_i\) 个数大于当前数,而 \(a_i\) 后面共有 \(n-i\) 个数大于 \(a_i\),则 \(a_i\) 前共有 \(i-a_i\) 个数大于 \(a_i\)

带删除

对于删除操作,我们需要再引入一个变量,时间戳。此处时间戳记录当前节点被删除的时间,假设我们已经完成了对时间戳的预处理,记为 \(del\) 数组,那么我们接下来要求的,就是当前点 \(i\) 之前有多少个数满足 \(i > j,a_i < a_j,del_i > del_j\),可以尝试使用三维偏序,这里不展开。

接着讨论删除过程,删除操作分为两种,一种是删除一个后缀最小值,另一种是删除一个非后缀最大值,分别进行分析。

  • 后缀最小值:当它是一个后缀最小值时,要消除它之前造成的所有影响,一方面我们从事先存下来的后缀最小值集合中删去这个点,同时删去其造成的额外代价,另一方面,我们要在集合中添加上新的后缀最小值。

  • 非后缀最小值:当它不是后缀最小值,减去他后面比他小的后缀最小值的数量。

我采用了树状数组进行维护,三个树状数组。

\(bit1\) 维护编号 \(1 \sim i\) 中没被删除的数的个数,

\(bit2\) 维护值 \(1 \sim i\) 中没被删除的数的个数,

\(bit3\) 维护区间后缀最小值个数。

Code

#include<bits/stdc++.h>
#define PII pair<int,int>
#define int long long
#define ls (o<<1)
#define rs (o<<1|1)
#define lowbit(a) ((a)&(-a))
#define mkp make_pair
using namespace std;
const int N=1e5+10;
int n,Q;
int a[N],tim[N],que[N];
bool vis[N],del[N];
vector<int> g[N];
set<PII> st;
int sum;
struct Binary_Tree {
	int c[N];
	void add(int i,int v) {
		for(; i<=n; i+=lowbit(i))c[i]+=v;
	}
	void build() {
		for(int i=1; i<=n; ++i)add(i,1);
	}
	int query(int i) {
		int res=0;
		for(; i; i^=lowbit(i))res+=c[i];
		return res;
	}
} bit1,bit2,bit3;
struct Segment_Tree {
	struct node {
		int l,r,c;
	} tr[N<<2];
	void pushup(int o) {
		tr[o].c=max(tr[ls].c,tr[rs].c);
	}
	void build(int o,int l,int r) {
		tr[o]= {l,r,0};
		if(l==r)return;
		int mid=tr[o].l+tr[o].r>>1;
		build(ls,l,mid),build(rs,mid+1,r);
	}
	void update(int o,int i,int x) {
		if(tr[o].l==tr[o].r)tr[o].c=x;
		else {
			int mid=tr[o].l+tr[o].r>>1;
			if(mid>=i)update(ls,i,x);
			else update(rs,i,x);
			pushup(o);
		}
	}
	int query(int o,int l,int r) {
		if(l<=tr[o].l&&tr[o].r<=r)return tr[o].c;
		int res=0;
		int mid=tr[o].l+tr[o].r>>1;
		if(mid>=l)res=max(res,query(ls,l,r));
		if(mid<r)res=max(res,query(rs,l,r));
		return res;
	}
} re;
signed main() {
	scanf("%lld%lld",&n,&Q);
	bit1.build();
	bit2.build();
	re.build(1,1,n);
	for(int i=1; i<=n; ++i)cin>>a[i],tim[i]=Q+1;
	for(int i=1; i<=Q; ++i)cin>>que[i],tim[que[i]]=i;
	for(int i=n; i>=1; --i)g[re.query(1,1,a[i]-1)].push_back(i),re.update(1,a[i],tim[i]);
	st.insert(mkp(0,0));
	st.insert(mkp(n+1,n+1));
	for(int i:g[0]) {
		sum+=i-a[i];
		bit3.add(i,1);
		vis[i]=1;
		st.insert(mkp(a[i],i));
	}
	printf("%lld ",sum+n);
	for(int i=1; i<=Q; ++i) {
		del[que[i]]=1;
		if(vis[que[i]]) {
			bit3.add(que[i],-1);
			sum-=bit1.query(que[i])-bit2.query(a[que[i]]);
			st.erase(mkp(a[que[i]],que[i]));
		} else sum-=bit3.query((--st.lower_bound(mkp(a[que[i]],0)))->second)-bit3.query(que[i]);
		bit1.add(que[i],-1);
		bit2.add(a[que[i]],-1);
		for(int j:g[i]) {
			if(del[j])continue;
			bit3.add(j,1);
			vis[j]=1;
			st.insert(mkp(a[j],j));
			sum+=bit1.query(j)-bit2.query(a[j]);
		}
		printf("%lld ",sum+n-i);
	}
	return 0;
}
posted @ 2025-08-01 18:23  黑昼白夜  阅读(12)  评论(0)    收藏  举报