• Codeforces 997E


    Portal

    题意:
    给出排列 (p_1,p_2,p_3,dots,p_n),定义一个区间 ([l,r]) 是好的当且仅当 (p_l,p_{l+1},p_{l+2},dots,p_r) 包含了连续的 (r-l+1) 个数。
    (q) 次询问,每次询问给出两个数 (l,r),求满足 (lleq xleq yleq r)([x,y]) 为好区间的 ((x,y)) 的个数。
    (n,qleq 1.2 imes 10^5)

    首先把“好区间”的定义翻译成人话就是 ((mx-mn)-(r-l)=0)
    不难想到将询问离线下来,记录到右端点上,然后用类似扫描线的方法求解。
    动态地枚举右端点 (r),建一棵线段树,线段树上下标为 (l) 的位置记录区间 ([l,r])((mx-mn)-(r-l)) 的值。
    当右端点从 (r-1) 变为 (r) 的时候,单调栈(sb 了 1/2)维护区间最大值和最小值的变化。同时在线段树上下标在 ([1,r-1]) 中的位置全部减 (1)(-(r-1-l)) 变为 (-(r-l))。以 (r) 为右端点的“好区间”的个数就是线段树上值为 (0) 的个数。注意到 线段树上位置为 (r) 的数总是 (0)(即区间 ([r,r]) 永远是好区间),所以最小值的个数就是线段树上值为 (0) 的个数。
    但此题要求的是右端点在 ([l,r]) 之间的好区间的个数,而不仅仅是右端点为 (r) 的好区间个数。(sb 了 2/2)所以我们线段树上新维护两个值:(sum)(cnt)(sum) 表示线段树上 ([l,r]) 这个区间历史上出现了多少次 (0)(cnt) 表示当前区间的 (0) 的个数要累加进答案多少次,i.e.,如果当前区间有 (x)(0),那么就要令 (sum+=x imes cnt)。当我们 pushdown 的时候,如果它左儿子的最小值等于当前区间的最小值,就把这个 (cnt) 标记传给它的左儿子。右儿子也同理。每次移动右端点的时候将整颗线段树的 (cnt) 值加 (1)。然后查询区间 ([l,r])(sum) 值之和就可以了。 注意到我们修改一个区间的时候是一路 pushdown 下来的,这意味着没有访问到的区间中 (0) 的个数不会发生变化,故其满足可乘性,所以这样搞没有问题。
    时间复杂度线性对数。

    #include <bits/stdc++.h>
    using namespace std;
    #define fi first
    #define se second
    #define fz(i,a,b) for(int i=a;i<=b;i++)
    #define fd(i,a,b) for(int i=a;i>=b;i--)
    #define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
    #define fill0(a) memset(a,0,sizeof(a))
    #define fill1(a) memset(a,-1,sizeof(a))
    #define fillbig(a) memset(a,63,sizeof(a))
    #define pb push_back
    #define ppb pop_back
    #define mp make_pair
    template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
    template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
    typedef pair<int,int> pii;
    typedef long long ll;
    template<typename T> void read(T &x){
    	x=0;char c=getchar();T neg=1;
    	while(!isdigit(c)){if(c=='-') neg=-1;c=getchar();}
    	while(isdigit(c)) x=x*10+c-'0',c=getchar();
    	x*=neg;
    }
    const int MAXN=1.2e5;
    int n,qu,a[MAXN+5];ll ans[MAXN+5];
    struct Query{int l,r;} q[MAXN+5];
    vector<int> qv[MAXN+5];
    struct node{
    	int l,r,mn,num,add_lz;
    	ll sum,cnt_lz;
    } s[MAXN*4+5];
    void pushup(int k){
    	s[k].mn=min(s[k<<1].mn,s[k<<1|1].mn);
    	s[k].num=(s[k<<1].num)*(s[k<<1].mn==s[k].mn)+(s[k<<1|1].num)*(s[k<<1|1].mn==s[k].mn);
    	s[k].sum=s[k<<1].sum+s[k<<1|1].sum;
    }
    void pushdown(int k){
    	if(s[k].add_lz){
    		s[k<<1].add_lz+=s[k].add_lz;s[k<<1].mn+=s[k].add_lz;
    		s[k<<1|1].add_lz+=s[k].add_lz;s[k<<1|1].mn+=s[k].add_lz;
    		s[k].add_lz=0;
    	}
    	if(s[k].cnt_lz){
    		if(s[k<<1].mn==s[k].mn) s[k<<1].cnt_lz+=s[k].cnt_lz,s[k<<1].sum+=s[k].cnt_lz*s[k<<1].num;
    		if(s[k<<1|1].mn==s[k].mn) s[k<<1|1].cnt_lz+=s[k].cnt_lz,s[k<<1|1].sum+=s[k].cnt_lz*s[k<<1|1].num;
    		s[k].cnt_lz=0;
    	}
    }
    void build(int k,int l,int r){
    	s[k].l=l;s[k].r=r;if(l==r){s[k].num=1;return;}
    	int mid=(l+r)>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
    	pushup(k);
    }
    void modify(int k,int l,int r,int x){
    	if(l>r) return;
    	if(l<=s[k].l&&s[k].r<=r){s[k].mn+=x;s[k].add_lz+=x;return;}
    	pushdown(k);int mid=(s[k].l+s[k].r)>>1;
    	if(r<=mid) modify(k<<1,l,r,x);
    	else if(l>mid) modify(k<<1|1,l,r,x);
    	else modify(k<<1,l,mid,x),modify(k<<1|1,mid+1,r,x);
    	pushup(k);
    }
    void addcnt(int k,int l,int r,int x){
    	if(l>r) return;
    	if(l<=s[k].l&&s[k].r<=r){
    		if(!s[k].mn) s[k].cnt_lz+=x,s[k].sum+=s[k].num;
    		return;
    	} pushdown(k);int mid=(s[k].l+s[k].r)>>1;
    	if(r<=mid) addcnt(k<<1,l,r,x);
    	else if(l>mid) addcnt(k<<1|1,l,r,x);
    	else addcnt(k<<1,l,mid,x),addcnt(k<<1|1,mid+1,r,x);
    	pushup(k);
    }
    int querynum(int k,int l,int r){
    	if(l<=s[k].l&&s[k].r<=r) return (!s[k].mn)*s[k].num;
    	pushdown(k);int mid=(s[k].l+s[k].r)>>1;
    	if(r<=mid) return querynum(k<<1,l,r);
    	else if(l>mid) return querynum(k<<1|1,l,r);
    	else return querynum(k<<1,l,mid)+querynum(k<<1|1,mid+1,r);
    }
    ll query(int k,int l,int r){
    	if(l<=s[k].l&&s[k].r<=r) return s[k].sum;
    	pushdown(k);int mid=(s[k].l+s[k].r)>>1;
    	if(r<=mid) return query(k<<1,l,r);
    	else if(l>mid) return query(k<<1|1,l,r);
    	else return query(k<<1,l,mid)+query(k<<1|1,mid+1,r);
    }
    int mx[MAXN+5],mxtp=0,mn[MAXN+5],mntp=0;
    int main(){
    	scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%d",&a[i]);scanf("%d",&qu);
    	for(int i=1;i<=qu;i++) scanf("%d%d",&q[i].l,&q[i].r),qv[q[i].r].pb(i);
    	build(1,1,n);
    	for(int i=1;i<=n;i++){
    		while(mntp&&a[mn[mntp]]>a[i]) modify(1,mn[mntp-1]+1,mn[mntp],a[mn[mntp]]-a[i]),mntp--;mn[++mntp]=i;
    		while(mxtp&&a[mx[mxtp]]<a[i]) modify(1,mx[mxtp-1]+1,mx[mxtp],a[i]-a[mx[mxtp]]),mxtp--;mx[++mxtp]=i;
    		modify(1,1,i-1,-1);addcnt(1,1,i,1);ffe(it,qv[i]) ans[*it]=query(1,q[*it].l,q[*it].r);
    //		for(int j=1;j<=i;j++) printf("%d %d %d
    ",j,i,querynum(1,j,i));
    	}
    	for(int i=1;i<=qu;i++) printf("%lld
    ",ans[i]);
    	return 0;
    }
    

    感觉跟 SP1557 有点像,当时还写了篇题解来着的?

  • 相关阅读:
    第一次博客作业
    编辑器、编译器、文件、IDE等常见概念辨析
    树、二叉树、查找知识点总结
    二叉排序树
    线性表知识点总结
    c语言文件
    第二次博客作业: 函数+进制转换器v1.0beta
    python作业1
    c语言知识
    第一次博客作业
  • 原文地址:https://www.cnblogs.com/ET2006/p/Codeforces-997E.html
Copyright © 2020-2023  润新知