2018.10.30【校内模拟】字胡串(单调栈)(容斥原理)

传送门


解析:

首先这道题卡空间,虽然说O(nlogn)O(nlogn)O(nlogn)的时间复杂度可过,但是要是空间复杂度不是O(n)O(n)O(n)可能就会咕咕咕。。。

思路:

首先,发现一个性质,就是满足条件的区间中必然有某个数在某个二进制位上有最大值没有的1。

那么我们可以预处理所有数在前缀(后缀)或的结果不变的情况下,向左和向右最远能够延伸的地方。

然后我们处理出每个数作为最大值向左向右能够延伸的最远地方。这个用单调栈来实现就行了。

然后就是统计了,我们考虑以最左最大值为标志统计这个区间,所以可以做到不重不漏,同时单调栈定左右端点的时候就会有所区别。

剩下的就是容斥原理了,每个点看它能够管辖的范围中能够有多少能够成为它合法的左右端点,再减去两端都合法算重的情况就行了。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline int getint(){
	re int num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

cs int N=1000006;

int bin[35];
int s[N],st[N],top;
int pre[N],suf[N];
int lbound[N],rbound[N];
int n;
ll ans;
signed main(){
	n=getint();
	for(int re i=1;i<=n;++i)s[i]=getint();
	
	for(int re i=1;i<=n;++i){
		
		while(top&&s[st[top]]<=s[i])--top;
		lbound[i]=st[top];
		st[++top]=i;
		
		for(int re j=0;(1<<j)<=s[i];++j){
			if((1<<j)&s[i])bin[j]=i;
			else pre[i]=max(pre[i],bin[j]);
		}
	}
	
	fill(bin,bin+35,n+1);
	st[top=0]=n+1;
	for(int re i=n;i;--i){
		while(top&&s[st[top]]<s[i])--top;
		rbound[i]=st[top];
		st[++top]=i;
		
		suf[i]=n+1;
		for(int re j=0;(1<<j)<=s[i];++j){
			if((1<<j)&s[i])bin[j]=i;
			else suf[i]=min(suf[i],bin[j]);
		}
	}
	
	for(int re i=1;i<=n;++i){
		if(pre[i]>lbound[i])ans+=(ll)(pre[i]-lbound[i])*(rbound[i]-i);
		if(suf[i]<rbound[i])ans+=(ll)(i-lbound[i])*(rbound[i]-suf[i]);
		if(pre[i]>lbound[i]&&suf[i]<rbound[i])ans-=(ll)(pre[i]-lbound[i])*(rbound[i]-suf[i]);
	}
	cout<<ans;
	return 0;
}

转载于:https://www.cnblogs.com/zxyoi/p/10047142.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值