• 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;
    }
    
    
  • 相关阅读:
    CKA考试题:[统计ready的node个数,不包含有污点的和没有调度的]
    期货-基差
    期货-套期保值
    CKA考试题:[init container]
    select 与 time.After 配合使用的问题
    kubernetes 学习资料
    [rook] rook的控制流
    k8s dev
    elastic search query & filter & query_string
    如何保持github的fork于主干同步
  • 原文地址:https://www.cnblogs.com/huayucaiji/p/NOIOL2022T1.html
Copyright © 2020-2023  润新知