Codeforces 526F Pudding Monsters

14 篇文章 0 订阅
2 篇文章 0 订阅

Problem

Codeforces

Solution

发现有些题目把情况分类讨论就可以使题目好处理很多。

注意到每行每列都只有一个怪物,那么我们可以将其转化到一维的情况,则可以得到一个排列,那么问题就变为了,统计有多少个子区间 [ L , R ] [L,R] [L,R]满足 m x − m n = R − L mx-mn=R-L mxmn=RL。好像很不好处理…

考虑跨越中点的区间,分类讨论一下 m x mx mx m n mn mn的分布:

m n , m x mn,mx mn,mx在左边,查 m x [ i ] − m n [ i ] = p − i mx[i]-mn[i]=p-i mx[i]mn[i]=pi,则 p = m x [ i ] − m n [ i ] + i p=mx[i]-mn[i]+i p=mx[i]mn[i]+i

若在都在右边,查 p = − m x [ i ] + m n [ i ] + i p=-mx[i]+mn[i]+i p=mx[i]+mn[i]+i

再考虑不在同一边的情况,比如 m n mn mn在左, m x mx mx在右,需要询问右边是否存在一个位置 p p p,满足 m x [ p ] − m n [ i ] = p − i mx[p]-mn[i]=p-i mx[p]mn[i]=pi,即 m n [ i ] − i = m x [ p ] − p mn[i]-i=mx[p]-p mn[i]i=mx[p]p

m x mx mx在左, m n mn mn在右,要询问左边是否存在一个位置 p p p,满足 m x [ p ] − m n [ i ] = i − p mx[p]-mn[i]=i-p mx[p]mn[i]=ip,即 − i − m n [ i ] = − m x [ p ] − p -i-mn[i]=-mx[p]-p imn[i]=mx[p]p

但要注意,分类讨论之后要注意判断左右两边的 m n mn mn m x mx mx是否满足相应的大小关系,而且对于mx和mn不在同一边时,注意到两边的最值具有单调性,所以合法的区间应该是一段连续的区间,可以用队列实现。

T ( n ) = 2 T ( n 2 ) + O ( n ) , T ( n ) = O ( n log ⁡ n ) T(n)=2T(\frac n 2)+O(n),T(n)=O(n\log n) T(n)=2T(2n)+O(n),T(n)=O(nlogn)

Code

#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn=300010,N=300005;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
struct tong{
	int a[maxn<<1];
	void insert(int x){++a[x];}
	void erase(int x){--a[x];}
	int query(int x){return a[x];}
}t;
int n,x,a[maxn],mn[maxn],mx[maxn];
ll ans;
int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x<y?x:y;}
void solve(int l,int r)
{
	if(l==r) return ;
	int m=(l+r)>>1,p,L,R;
	mx[m]=mn[m]=a[m];mx[m+1]=mn[m+1]=a[m+1];
	for(int i=m-1;i>=l;i--){mx[i]=max(mx[i+1],a[i]);mn[i]=min(mn[i+1],a[i]);}
	for(int i=m+2;i<=r;i++){mx[i]=max(mx[i-1],a[i]);mn[i]=min(mn[i-1],a[i]);}
	L=R=m+1;
	for(int i=m;i>=l;i--)//左小右大
	{
		p=mx[i]-mn[i]+i;
		if(m<p&&p<=r&&mn[p]>mn[i]&&mx[p]<mx[i]) ++ans;
		while(R<=r&&mn[i]<mn[R]) t.insert(mx[R]-R+N),++R;
		while(L<R&&mx[i]>mx[L]) t.erase(mx[L]-L+N),++L;
		ans+=t.query(mn[i]-i+N);
	}
	while(L<R) t.erase(mx[L]-L+N),++L;
	L=R=m;
	for(int i=m+1;i<=r;i++)//左大右小
	{
		p=-mx[i]+mn[i]+i;
		if(l<=p&&p<=m&&mn[p]>mn[i]&&mx[p]<mx[i]) ++ans;
		while(l<=L&&mn[i]<mn[L]) t.insert(mx[L]+L),--L;
		while(L<R&&mx[i]>mx[R]) t.erase(mx[R]+R),--R;
		ans+=t.query(mn[i]+i);
	}
	while(L<R) t.erase(mx[R]+R),--R;
	solve(l,m);solve(m+1,r);
}
int main()
{
	read(n);ans=n;
	for(int i=1;i<=n;i++){read(x);read(a[x]);}
	solve(1,n);
	printf("%lld\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值