NOIOL2020 T1丹钓战

NOIOL2020 T1丹钓战

前言

来,看我看我,我宣布个事哦!我是个煞*。

解题过程

这道题我想分享一下我做题的过程和心路历程,实在曲折。

首先可以一眼看出的是,我们可以通过求对于任意的 \(i\),这个二元组可以直接或间接弹出其前面的二元组中最小下标,我们定义这个量为 \(lft_i\)。何为间接呢?我们看样例一:

10 4
3 1 3 1 2 3 3 2 1 1
10 10 2 9 7 5 4 7 6 1
1 4
7 8
7 10
1 8

对于第 \(4\) 个二元组,它可以直接弹出 \(2,3\),但是没法弹出 \(1\)。我们可以发现 \(2\) 能弹出 \(1\),也就是说,\(4\) 可以间接弹出 \(1\)

我们该如何求 \(lft_i\) 呢?我们再定义一个 \(brd_i\) 表示第 \(i\) 个二元组可以直接弹出的前面二元组中的最小下标。我们可以发现:\(lft_i=\min\limits_{j=brd_i}^i lft_i\)。这个很容易理解,线段树求解即可。

现在问题转变成了求解 \(brd_i\)。我们可以想到用二分答案来解决。问题就转变到了如何解决 \(\operatorname{check}\) 函数了。

若对于位置 \(j\) 可以被 \(i\) 直接弹出,我们有等式:\(\sum\limits_{k=j}^i [b_i<b_k]=\sum\limits_{k=j}^i [b_i<b_k\ \&\ a_i=a_k]\)。这是显然的。我们 \(\operatorname{check}\) 函数就基于这个式子。左边和右边可以分别用主席树计算。左边很好理解,对于右边,可以对每个 \(a_i\),以其为下标构建一颗主席树,其节点数总和不会很大,还是在 \(O(n\log n)\) 级别的。

最后,我们知道了 \(lft_i\),如何求答案呢?对于询问 \([l,r]\),答案就是 \(\sum\limits_{i=l}^r [lft_i\leq l]\)。这个结构可以用主席树维护,不多赘述。

这个算法时间复杂度为 \(O(n\log^2 n)\)。代码量 200+。

于是在考试时我用 vector 代替了主席树中的数组,喜提 \(0pts\)。调试时发现给出了一个 SIGTRAP的信号。后来上网查了一下资料发现,vector 和递归会产生一些奇妙的反应,导致程序死掉。

于是赛后我用数组重写了这个程序,把所有的节点放到一个数组里就可以了。我觉得因为这个程序卡不满时间复杂度,所以认为 \(O(n\log^2 n)\) 可以通过,直到我测了大样例……开 O2 跑了 10s。效率有点低。一交发现出题人卡时间卡的很死,\(50pts\) 一分不多给。

于是我开始思考,真的出问题了吗?我们发现时间复杂度的瓶颈在求解 \(brd_i\) 上。我思前想后发现,\(brd_i\) 的求解已经无法加速了。

我们可以越过 \(brd_i\) 直接求 \(lft_i\) 吗?

答案是,可以的。我们发现 \(lft_i\) 其实就是我们从 \(1\) 开始模拟这个入栈出栈的过程在 \(i\) 及间入栈时栈顶元素编号。这样我们可以 \(O(n)\) 求出 \(lft_i\)

进一步的,我们其实可以用树状数组来维护答案式,也就是说,代码量骤减至 \(60\) 行……

所以理解我在前言中的话了吧……

代码

纪念一下自己写的主席树,就不想改了。

//Don't act like a loser.
//This code is written by huayucaiji
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
using namespace std;

int read() {
	char ch=getchar();
	int f=1,x=0;
	while(ch<'0'||ch>'9') {
		if(ch=='-')
			f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9') {
		x=x*10+ch-'0';
		ch=getchar();
	}
	return f*x;
}
char read_char() {
	char ch=getchar();
	while(!isalpha(ch)) {
		ch=getchar();
	}
	return ch;
}

const int MAXN=5e5+10;

int n,q,tot,cnt;
int a[MAXN],b[MAXN],num[MAXN],m[MAXN],brd[MAXN],id[MAXN],pre[MAXN],rt[MAXN<<2];
vector<int> idx[MAXN];

struct seg {
	int sum,ls,rs;
}s[MAXN*73],t[MAXN<<2];

int newnode() {
	tot++;
	return tot;
}
	
void build(int l,int r,int &p) {
	if(!p) {
		p=newnode();
	}
	if(l==r) {
		return ;
	}
	int mid=(l+r)>>1;
	build(l,mid,s[p].ls);
	build(mid+1,r,s[p].rs);
}

int modify(int l,int r,int pp,int &p,int x) {
	if(p>tot) {
		int ttt;
		ttt++;
	}
	if(!p) {
		p=newnode();
	}
	if(l==r) {
		s[p].sum=s[pp].sum+1;
		return p;
	}
	int mid=(l+r)>>1;
	if(x<=mid) {
		s[p].rs=s[pp].rs;
		s[p].ls=modify(l,mid,s[pp].ls,s[p].ls,x);
	}
	else {
		s[p].ls=s[pp].ls;
		s[p].rs=modify(mid+1,r,s[pp].rs,s[p].rs,x);
	}
	s[p].sum=s[s[p].ls].sum+s[s[p].rs].sum;
	return p;
}

int query(int l,int r,int p1,int p2,int x,int y) {
	if(y<l||r<x) {
		return 0;
	}
	if(x<=l&&r<=y) {
		return s[p2].sum-s[p1].sum;
	}
	int mid=(l+r)>>1;
	return query(l,mid,s[p1].ls,s[p2].ls,x,y)+query(mid+1,r,s[p1].rs,s[p2].rs,x,y);
}

stack<int> stk;

int main() {
	cin>>n>>q;
	for(int i=1;i<=n;i++) {
		a[i]=read();
		idx[a[i]].push_back(i);
	}
	for(int i=1;i<=n;i++) {
		b[i]=read();
	}
	
	for(int i=1;i<=n;i++) {
		while(!stk.empty()&&!(a[i]!=a[stk.top()]&&b[i]<b[stk.top()])) {
			stk.pop();
		}
		if(stk.empty()) {
			brd[i]=1;
		}
		else {
			brd[i]=stk.top()+1;
		}
		stk.push(i);
	}
	
	for(int i=1;i<=n;i++) {
		modify(1,n,rt[2*n+i-1],rt[2*n+i],brd[i]);
	}
	while(q--) {
		int l,r;
		l=read();r=read();
		int ans=query(1,n,rt[2*n+l-1],rt[2*n+r],1,l);
		printf("%d\n",ans);
	}
	
	return 0;
}

posted @ 2022-03-27 13:53  huayucaiji  阅读(276)  评论(1编辑  收藏  举报