zoukankan      html  css  js  c++  java
  • CF526F Pudding Monsters

    题目链接

    题意分析

    我们把所有的坐标按照行进行排序 然后列就成为了一个排列

    把题意进行简单的转化之后就是 求排列中有多少个子串 且该子串排序之后是公差为1的等差数列

    这道题其实真的很锻炼思维

    首先 对于一个字串([L,R]) 其中的最大值以及最小值为(Max,Min) 那么显而易见的就是(Max-Min=R-L)

    也就是(Max-Min+L-R=0)

    一般来说 这种问题我们会考虑扫描右端点 然后每一次扫描求出有多少个符合要求的左端点

    由于是排列 所以(Max-Min+L-R≥0) 所以我们只需要求左边对应的最小值是否为零 已经为零的数量

    考虑使用线段树来维护当前(Max-Min+L-R) 但是具体到(Max),(Min),(L),(R)的维护

    (L):由于变的只是右端点 左端点是不变的 所以我们一开始就可以直接维护上其贡献

    (R):右端点每一次移动都会+1 所以我们直接每一次之后对所有都来个-1就可以了

    (Max)以及(Min):动态移动维护前缀最小值 这不就是单调栈一贯的用法吗?

    以维护最大值为例 我们维护一个单调递减的单调栈 假设单调栈的栈顶为top 对应栈顶值为是sta[top]

    当前如果出现一个值now大于栈顶元素之后 那么栈元素sta[top-1]对应的位置是le sta[top]对应的位置是ri

    [le+1,ri]都要消去最大值sta[top]的影响,而加上新的最大值now的影响

    维护最小值同样如此

    CODE:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #define N 300080
    using namespace std;
    int n,tpx,tpy;
    long long ans;
    int num[N],stx[N],sty[N];
    struct Node
    {
    	int Min,tag;long long cnt;
    }tre[N<<2];
    void pushup(int now)
    {
    	if(tre[now<<1].Min<tre[now<<1|1].Min)
    	{
    		tre[now].Min=tre[now<<1].Min;
    		tre[now].cnt=tre[now<<1].cnt;
    	}
    	else if(tre[now<<1|1].Min<tre[now<<1].Min)
    	{
    		tre[now].Min=tre[now<<1|1].Min;
    		tre[now].cnt=tre[now<<1|1].cnt; 
    	}
    	else if(tre[now<<1].Min==tre[now<<1|1].Min)
    	{
    		tre[now].Min=tre[now<<1].Min;
    		tre[now].cnt=tre[now<<1].cnt+tre[now<<1|1].cnt;
    	}
    }
    void down(int now)
    {
    	tre[now<<1].Min+=tre[now].tag;
    	tre[now<<1|1].Min+=tre[now].tag;
    	tre[now<<1].tag+=tre[now].tag;
    	tre[now<<1|1].tag+=tre[now].tag;
    	tre[now].tag=0;
    }
    void build(int now,int le,int ri)
    {
    	tre[now].tag=0;
    	if(le==ri) {tre[now].Min=le;tre[now].cnt=1;return;}
    	int mid=(le+ri)>>1;
    	build(now<<1,le,mid);build(now<<1|1,mid+1,ri);
    	tre[now]=tre[now<<1];
    }
    void update(int now,int lenow,int rinow,int le,int ri,int d)
    {
    	if(le<=lenow&&rinow<=ri)
    	{
    		tre[now].Min+=d;
    		tre[now].tag+=d;
    		return;
    	}
    	if(tre[now].tag) down(now);
    	int mid=(lenow+rinow)>>1;
    	if(le<=mid) update(now<<1,lenow,mid,le,ri,d);
    	if(mid<ri) update(now<<1|1,mid+1,rinow,le,ri,d);
    	pushup(now);
    }
    long long qury(int now,int lenow,int rinow,int le,int ri)
    {
    	if(le<=lenow&&rinow<=ri)
    	{
    		if(tre[now].Min==0) return tre[now].cnt;
    		else return 0;
    	}
    	if(tre[now].tag) down(now);
    	int mid=(lenow+rinow)>>1;long long tmp=0;
    	if(le<=mid) tmp+=qury(now<<1,lenow,mid,le,ri);
    	if(mid<ri) tmp+=qury(now<<1|1,mid+1,rinow,le,ri);
    	return tmp;
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1,x,y;i<=n;++i) scanf("%d%d",&x,&y),num[x]=y;
    	build(1,1,n);
    	for(int i=1;i<=n;++i)
    	{
    		update(1,1,n,1,n,-1);
    		while(tpx&&num[stx[tpx]]<num[i])
    		update(1,1,n,stx[tpx-1]+1,stx[tpx],num[i]-num[stx[tpx]]),--tpx;
                    //这里其实一开始的时候有些疑问
                    //按照差分的话 我们没有加上原最大值 那么差分累加之后的出现的仍是一个差值而不是一个元素值
                    //后来发现在区间[L,L]中 L-L=L-L+Max-Min
                    //而后来的区间都是由此扩展而来 所以我们直接累加移动带来的差值贡献即可      
    		stx[++tpx]=i;
    //		printf("now [max]=%d
    ",tpx);
    		while(tpy&&num[sty[tpy]]>num[i])
    		update(1,1,n,sty[tpy-1]+1,sty[tpy],num[sty[tpy]]-num[i]),--tpy;
    		sty[++tpy]=i;
    		ans+=qury(1,1,n,1,i);
    //		printf("now [min]=%d
    ",tpy);
    //		printf("now at %d = %lld
    ",i,ans);
    	}
    	printf("%lld
    ",ans);
    	return 0;
    } 
    
  • 相关阅读:
    PLAYBOOK 命令统计资源利用率 输出本地文件 flask展示
    如何对PFX证书转换成PEM格式证书
    Openshift学习
    Playbook handlers使用
    网络空间安全基础篇(2)----wireshark
    网络空间安全基础篇(1)----nmap的使用
    opencv——图像掩码操作
    opencv图像阈值操作
    在图片上绘制图形和添加文字
    使用opencv去操作树莓派摄像头保存图片和视频
  • 原文地址:https://www.cnblogs.com/LovToLZX/p/13850144.html
Copyright © 2011-2022 走看看