zoukankan      html  css  js  c++  java
  • CodeForces 193D. Two Segments【线段树】

    传送门
    补这道题的契机是因为烂桥杯2013的最后一题,虽然那道题暴力也能过,但看到大佬介绍的线段树做法,感觉又刷新了我对于线段树的认识,果然线段树是无所不能的

    题意

    给你一个 (n) 的全排列 (A),你可以从中选两个不重合的区间,如果这两个区间里的所有数按升序排列是一个公差为 (1) 的等差数列,则是一种合法方案,若两组不同区间包含的数相同,则视为一种方案。问一共有多少种合法方案。

    题解

    考虑如果已知正整数区间 ([l,r]) 在原全排列 (A) 中被分为 (k) 段,此时加入 (r+1),有如下情况:

    • 如果 (r+1)([l,r]) 中任意一个数都不相邻,那么 ([l,r+1]) 被分为 (k+1) 段。
    • 如果 (r+1)([l,r]) 中一个数相邻,那么 ([l,r+1]) 依然被分为 (k) 段。
    • 如果 (r+1)([l,r]) 中两个数相邻,那么 ([l,r+1]) 被分为 (k-1) 段。

    既然这样可以得到一个 (O(n^2)) 的算法,这里就不赘述了。因为这道题的数据范围是 (nle 300\,000)(O(n^2)) 的算法是接受不了的。
    但是根据上述的分段变化规律,我们发现可以对区间进行更改,也就是说,以 (f[l,r]) 表示 ([l,r]) 被拆分的段数,如果已知 (f[1,r],f[2,r],...,f[r,r]),这时对这些区间都加入 (r+1),有以下情况:

    • 如果在 ([1,r]) 中没有数与 (r+1)(A) 中相邻,那么:

    [f[i,r+1]=f[i,r]+1 ]

    • 如果在 ([1,r]) 有只一个数 (x)(r+1)(A) 中相邻,([i,r+1](1le ile x)) 被分的段数会和 ([i,r]) 一样,因为 (r+1) 是和 (x) 紧紧贴♂在一起的嘛;而 ([i,r+1](x+1le ile r+1))([i,r]) 所分的段数多 (1),原因就是 ([i,r]) 中没有数和 (r+1) 相邻,那么 (r+1) 会单独构成一段。所以这种情况可以表示为:

    [egin{aligned} f[i,r+1] & = f[i,r] &, &&1le &ile x\ f[i,r+1] & = f[i,r]+1 &, &&x+1le &ile r+1 end{aligned} ]

    • 如果在 ([l,r]) 中有两个数 (x,y(x<y))(r+1)(A) 中相邻,那么 ([i,r+1](1le ile x)) 被分的段数比 ([i,r])(1),因为本来 (x,y) 是裂开的,加入 (r+1) 后这两段合起来了;而 ([i,r+1](x+1le ile y))([i,r]) 相等,因为只有 (y) 一个数和 (r+1) 贴;而 ([i,r+1](y+1le ile r+1))([i,r])(1)。可表示为:

    [egin{aligned} f[i,r+1] &= f[i,r]-1 &, &&1le &ile x\ f[i,r+1] &= f[i,r] &, &&x+1le &ile y\ f[i,r+1] &= f[i,r]+1 &, &&y+1le &ile r+1 end{aligned} ]

    所以我们就可以用线段树来维护决定 (r)(f[i,r]) 的值,根据题意,每次加入 (r+1) 之后,只需要查询线段树中值为 (1)(2) 的叶节点的个数就行了。

    简单说一下线段树的写法

    因为根据 (f[l,r]) 的性质就可以知道线段树叶节点的最小值就是 (1),那么要查 (1)(2) 的个数,只需要维护区间最小值和最小值的个数和最小值 (+1) 的个数就行了。
    注意 pushup 操作的写法,统计区间最小值,然后通过最小值查询左右子树中最小值的个数,尤其不要忘了统计全最小值 (+1) 的个数。

    代码

    #include <iostream>
    #include <stdio.h>
    using namespace std;
    typedef long long LL;
    const int N=3e5+10;
    int n,a[N],p[N];
    
    struct SegTree{
    	#define mid (l+r>>1)
    	int num1[N*4],num2[N*4],minv[N*4],tag[N*4];
    	void pushdown(int id){
    		minv[id<<1]+=tag[id];tag[id<<1]+=tag[id];
    		minv[id<<1|1]+=tag[id];tag[id<<1|1]+=tag[id];
    		tag[id]=0;
    	}
    	void pushup(int id){
    		minv[id]=min(minv[id<<1],minv[id<<1|1]);
    		num1[id]=num2[id]=0;
    		if(minv[id<<1]==minv[id]) num1[id]+=num1[id<<1],num2[id]+=num2[id<<1];
    		else if(minv[id<<1]==minv[id]+1) num2[id]+=num1[id<<1];
    		if(minv[id<<1|1]==minv[id]) num1[id]+=num1[id<<1|1],num2[id]+=num2[id<<1|1];
    		else if(minv[id<<1|1]==minv[id]+1) num2[id]+=num1[id<<1|1];
    	}
    	void build(int id,int l,int r){
    		num1[id]=r-l+1;
    		if(l==r) return;
    		build(id<<1,l,mid);
    		build(id<<1|1,mid+1,r);
    	}
    	void upd(int id,int l,int r,int L,int R,int x){
    		if(L<=l&&r<=R) {minv[id]+=x;tag[id]+=x;return;}
    		if(tag[id]) pushdown(id);
    		if(L<=mid) upd(id<<1,l,mid,L,R,x);
    		if(R>mid) upd(id<<1|1,mid+1,r,L,R,x);
    		pushup(id);
    	}
    	int ask(int id,int l,int r,int L,int R){
    		if(L<=l&&r<=R) {
    			if(minv[id]==1) return num1[id]+num2[id];
    			if(minv[id]==2) return num1[id];
    			return 0;
    		}
    		if(tag[id]) pushdown(id);
    		int res=0;
    		if(L<=mid) res+=ask(id<<1,l,mid,L,R);
    		if(R>mid) res+=ask(id<<1|1,mid+1,r,L,R);
    		return res;
    	}
    	#undef mid
    }tr;
    
    int main(){
    	scanf("%d",&n);
    	for(int i=1,x;i<=n;i++) scanf("%d",&x),p[x]=i;
    	LL ans=0;
    	tr.build(1,1,n);
    	for(int i=1;i<=n;i++){
    		a[p[i]]=i;
    		int x=a[p[i]-1],y=a[p[i]+1];
    		if(x>y) swap(x,y);
    		if(x) tr.upd(1,1,n,1,x,-1),tr.upd(1,1,n,y+1,i,1);
    		else if(y) tr.upd(1,1,n,y+1,i,1);
    		else tr.upd(1,1,n,1,i,1);
    		ans+=tr.ask(1,1,n,1,i);
    		//cout<<"->"<<ans<<endl;
    	}
    	ans-=n;
    	cout<<ans<<endl;
    	return 0;
    }
    
  • 相关阅读:
    排序算法 快速排序l两种算法和堆排序
    VC之那些 strcpy 往事
    mysql常用命令小结
    pymongo学习第1篇——增删改查
    通过IntelliJ IDEA创建maven+springmvc+mybatis项目
    python爬虫2——下载文件(中华网图片库下载)
    biz_platform项目过程
    python爬虫1——获取网站源代码(豆瓣图书top250信息)
    一些不错的网址收藏
    git常用操作
  • 原文地址:https://www.cnblogs.com/BakaCirno/p/12466819.html
Copyright © 2011-2022 走看看