zoukankan      html  css  js  c++  java
  • 【LOJ6062】「2017 山东一轮集训 Day2」Pair(线段树套路题)

    点此看题面

    大致题意: 给出一个长度为(n)的数列(a)和一个长度为(m)的数列(b),求(a)有多少个长度为(m)的子串与(b)匹配。数列匹配指存在一种方案使两个数列中的数两两配对,数配对指它们的和不小于(h)

    预处理

    显然,要判断两个数列是否匹配,肯定是将一个数列从小到大排序,另一个数列从大到小排序,然后逐一判断相应位置上的两个数是否配对。

    我们可以将其转化,把(b_i)变成(h-b_i),然后将(a)的某个子串和(b)都从大到小排序,再比较相应位置上(a_i)是否大于等于(b_i),即可判断是否匹配。

    线段树优化

    然后就可以发现这是一道线段树套路题。

    考虑要对应位置(a_i)大于等于(b_i),且(b_1ge b_2ge b_3ge...ge b_n),则对于(b_i),应该有(ge i)个数大于等于它。

    所以,我们可以将(a,b)离散化,对于每个(b_i),在线段树第(b_i)个位置上减(i),对于每个(a_i),在线段树第([1,a_i])这个区间上加(1)

    因此,判断线段树中是否所有元素大于等于(0)即可判断两个序列是否匹配(这只须记录一个最小值就能判)。

    要注意的是,对于重复的(b_i),我们只选择最大的(i)减去,而不是叠加。

    既然这样,我们枚举(a)中子串右端点(i),每次删去第(i-m)个数,然后加上第(i)个数,若当前情况合法则将(ans)(1)即可。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 150000
    #define min(x,y) ((x)<(y)?(x):(y))
    using namespace std;
    int n,m,h,dc,a[N+5],b[N+5],dv[N+5];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define tn (x<<3)+(x<<1)
    		#define D isdigit(c=tc())
    		char c,*A,*B,FI[FS];
    	public:
    		I FastIO() {A=B=FI;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
    		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    }F;
    class SegmentTree//线段树
    {
    	private:
    		#define L l,mid,rt<<1,tl,tr
    		#define R mid+1,r,rt<<1|1,tl,tr
    		#define PU(x) (O[x]=O[x<<1]+O[x<<1|1])
    		#define PD(x) (O[x].F&&(O[x<<1]+=O[x].F,O[x<<1|1]+=O[x].F,O[x].F=0))
    		struct node//存储节点信息
    		{
    			int Mn,F;I node(CI mn=0,CI f=0):Mn(mn),F(f){}
    			I node operator + (Con node& o) Con {return node(min(Mn,o.Mn));}
    			I void operator += (CI x) {Mn+=x,F+=x;}
    		}O[N<<2];
    		I void upt(CI l,CI r,CI rt,CI tl,CI tr,CI v)//区间修改
    		{
    			if(tl<=l&&r<=tr) return O[rt]+=v;PD(rt);RI mid=l+r>>1;
    			tl<=mid&&(upt(L,v),0),tr>mid&&(upt(R,v),0),PU(rt);
    		}
    	public:
    		I void Update(CI l,CI r,CI v) {upt(1,n,1,l,r,v);}
    		I bool Check() {return O[1].Mn>=0;}//判断最小值是否大于等于0
    }S;
    I int GV(CI x)//求出离散化后的值
    {
    	RI l=1,r=dc,mid;W(l<=r) dv[mid=l+r>>1]<x?l=mid+1:r=mid-1;
    	return l;
    }
    int main()
    {
    	RI i,ans=0;for(F.read(n,m,h),i=1;i<=m;++i) F.read(b[i]);//读入
    	for(sort(b+1,b+m+1),i=1;i<=n;++i) F.read(a[i]),dv[i]=a[i];
    	sort(dv+1,dv+n+1),dc=unique(dv+1,dv+n+1)-dv-1;for(i=1;i<=n;++i) a[i]=GV(a[i]);//离散化
    	for(i=1;i<=m;++i) b[i]=GV(h-b[i]),b[i]<h&&(S.Update(b[i],b[i],b[i]^b[i-1]?-i:-1),0);//离散化,并在线段树上初始化,注意判重
    	for(i=1;i<=m;++i) S.Update(1,a[i],1);ans+=S.Check();//将前m个数加入,更新ans
    	for(i=m+1;i<=n;++i) S.Update(1,a[i-m],-1),S.Update(1,a[i],1),ans+=S.Check();//每次删除第i-m个数,加上第i个数,然后更新ans
    	return printf("%d",ans),0;//输出答案
    }
    
  • 相关阅读:
    MySQL基础知识总结
    PHP常见算法
    PHP程序功能设计
    SVN配置使用及移植
    推荐一个SpringBoot + Vue + MyBatis 音乐网站项目
    累积sql常用查询语句「Oracle」
    Nginx服务器设置http/https正向代理,使用ngx_http_proxy_connect_module模块
    squid配置文件
    nginx命令
    k8s与Docker有啥关系
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/LOJ6062.html
Copyright © 2011-2022 走看看