zoukankan      html  css  js  c++  java
  • BZOJ 4527: K-D-Sequence

    4527: K-D-Sequence

    Time Limit: 20 Sec  Memory Limit: 256 MB
    Submit: 163  Solved: 66
    [Submit][Status][Discuss]

    Description

    我们称一个数列为一个好的k-d数列,当且仅当我们在其中加上最多k个
    数之后,数列排序后为一个公差为d的等差数列。
    你手上有一个由n个整数组成的数列a。你的任务是找到它的最长连续子
    串,使得满足子串为好的k-d数列。

    Input

    第一行包含三个用空格隔开的整数n,k,d(1<=n<=2*10^5;0<=k<=
    2*10^5;0<=d<=10^9)。第二行包含n个空格隔开的整数:a1,a2,...,an(-10^9<=ai<=10^9)表示数列a。

    Output

    输出两个用空格隔开的整数L,r(1<=L<=r<=n),表示数列a_L,a_L+1,...,
    ar是好k-d数列的子串中最长的。
    如果有多个最优答案,输出那个L值最小的。
     

    Sample Input

    6 1 2
    4 3 2 8 6 2

    Sample Output

    3 5
    //第一个测试样例的答案为包括数字 2,8,6 的子串——在加入数字 4 并且
    排序之后,它变成了数列 2,4,6,8——公差为 2 的等差数列。

    HINT

    Source

    分析:

    考虑如果一个数列可以成为等差数列,其必要条件是任意$a[i]$%$d$都是相等的,也就是说,我们要找出连续的最长一段同余子串,使得其满足可以成为等差数列...

    接的我们发现,可以把公差为$d$转化为公差为$1$,也就是满足$max(frac{a[i]}{d})-min(frac{a[i]}{d})+1-(j-i+1)<=k$,感觉还是很好理解的...

    所以我们考虑枚举子串的右端点,然后找到最靠左的满足要求的点,发现我们可以用线段树来维护信息,维护三个值,$Min$代表的是最小的$min+l$,$Max$代表的是最小的$max+l$,$Sum$代表的是最小的$min+max+l$,然后我们每把右端点右移一位,就把当前的点影响的区间修改...

    代码:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<map>
    //by NeighThorn
    #define inf 2e9+7
    using namespace std;
    
    const int maxn=200000+5;
    
    int n,k,d,top,ans,ansl,ansr,a[maxn],b[maxn],c[maxn],pre[maxn],stk[maxn],lmax[maxn],lmin[maxn];
    
    map<int,int> mp;
    
    struct M{
    	
    	int l,r;
    	long long Min,Max,Sum,tagMax,tagMin;	
    	
    	inline void init(void){
    		Min=Max=Sum=tagMin=tagMax=inf;
    	}
    	
    }tree[maxn<<2];
    
    inline void pushdown(int tr,int l,int r,int mid){
    	int val;
    	if(tree[tr].tagMax!=inf)
    		val=tree[tr].tagMax,
    		tree[tr<<1].Max=val+l,
    		tree[tr<<1].tagMax=val,
    		tree[tr<<1].Sum=val+tree[tr<<1].Min,
    		tree[tr<<1|1].Max=val+mid+1,
    		tree[tr<<1|1].tagMax=val,
    		tree[tr<<1|1].Sum=val+tree[tr<<1|1].Min,
    		tree[tr].tagMax=inf;
    	if(tree[tr].tagMin!=inf)
    		val=tree[tr].tagMin,
    		tree[tr<<1].tagMin=val,
    		tree[tr<<1].Min=-val+l,
    		tree[tr<<1].Sum=-val+tree[tr<<1].Max,
    		tree[tr<<1|1].tagMin=val,
    		tree[tr<<1|1].Min=-val+mid+1,
    		tree[tr<<1|1].Sum=-val+tree[tr<<1|1].Max,
    		tree[tr].tagMin=inf;
    }
    
    inline void update(int tr){
    	tree[tr].Min=min(tree[tr<<1].Min,tree[tr<<1|1].Min);
    	tree[tr].Max=min(tree[tr<<1].Max,tree[tr<<1|1].Max);
    	tree[tr].Sum=min(tree[tr<<1].Sum,tree[tr<<1|1].Sum);
    }
    
    inline void build(int l,int r,int tr){
    	tree[tr].l=l,tree[tr].r=r,tree[tr].init();
    	if(l==r){
    		tree[tr].Max=c[l]+l,tree[tr].Min=-c[l]+l,tree[tr].Sum=l;
    		return;
    	}
    	int mid=(l+r)>>1;
    	build(l,mid,tr<<1);build(mid+1,r,tr<<1|1);
    	update(tr);
    }
    
    inline void change(int l,int r,int id,int val,int tr){
    	if(tree[tr].l==l&&tree[tr].r==r){
    		if(id==0) tree[tr].tagMax=val,tree[tr].Max=val+tree[tr].l,tree[tr].Sum=tree[tr].Min+val;
    		if(id==1) tree[tr].tagMin=val,tree[tr].Min=-val+tree[tr].l,tree[tr].Sum=tree[tr].Max-val;
    		return;
    	}
    	int mid=(tree[tr].l+tree[tr].r)>>1;
    	pushdown(tr,tree[tr].l,tree[tr].r,mid);
    	if(r<=mid)
    		change(l,r,id,val,tr<<1);
    	else if(l>mid) 
    		change(l,r,id,val,tr<<1|1);
    	else
    		change(l,mid,id,val,tr<<1),change(mid+1,r,id,val,tr<<1|1);
    	update(tr);
    }
    
    inline int find(int tr,int val){
    	if(tree[tr].Sum>val) return n+1;
    	if(tree[tr].l==tree[tr].r) return tree[tr].l;
    	pushdown(tr,tree[tr].l,tree[tr].r,tree[tr].l+tree[tr].r>>1);
    	if(val>=tree[tr<<1].Sum) return find(tr<<1,val);
    	return find(tr<<1|1,val);
    }
    
    inline int query(int l,int r,int tr,int val){
    	if(tree[tr].l==l&&tree[tr].r==r) return find(tr,val);
    	int mid=(tree[tr].l+tree[tr].r)>>1;
    	pushdown(tr,tree[tr].l,tree[tr].r,mid);
    	if(r<=mid) return query(l,r,tr<<1,val);
    	else if(l>mid) return query(l,r,tr<<1|1,val);
    	else{
    		int lala=query(l,mid,tr<<1,val);
    		if(lala==n+1)
    			return query(mid+1,r,tr<<1|1,val);
    		return lala;
    	}
    }
    
    inline void solve(void){
    	for(int i=1,j;i<=n;i=j+1){
    		for(j=i;j<n&&a[j]==a[j+1];j++);
    		if(j-i+1>ans) ans=j-i+1,ansl=i,ansr=j;
    	}
    	printf("%d %d
    ",ansl,ansr);
    }
    
    signed main(void){
    #ifndef ONLINE_JUDGE
    	freopen("in.txt","r",stdin);
    #endif
    	scanf("%d%d%d",&n,&k,&d);
    	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    	for(int i=1;i<=n;i++) a[i]+=1e9;ans=ansl=ansr=1;
    	if(d==0) return solve(),0;
    	for(int i=1;i<=n;i++){
    		if(mp.find(a[i])==mp.end()) mp[a[i]]=i,pre[i]=0;
    		else pre[i]=mp[a[i]],mp[a[i]]=i;
    	}
    	for(int i=1;i<=n;i++) b[i]=a[i]%d,c[i]=a[i]/d;
    	build(1,n,1);
    	for(int i=n;i>=1;i--){
    		while(top&&c[stk[top]]>=c[i]) lmin[stk[top--]]=i;
    		stk[++top]=i;
    	}
    	while(top) lmin[stk[top--]]=0;
    	for(int i=n;i>=1;i--){
    		while(top&&c[stk[top]]<=c[i]) lmax[stk[top--]]=i;
    		stk[++top]=i;
    	}
    	while(top) lmax[stk[top--]]=0;
    	for(int i=1,j;i<=n;i=j+1){
    		for(j=i;j<n&&b[j]==b[j+1];j++);
    		for(int t=i,tmp;t<=j;t++){
    			if(pre[t]>=i){
    				j=pre[t];
    				break;
    			}
    			change(max(i,lmax[t]+1),t,0,c[t],1),
    			change(max(i,lmin[t]+1),t,1,c[t],1);
    			tmp=query(i,t,1,k+t);
    			if(t-tmp+1>ans) ans=t-tmp+1,ansl=tmp,ansr=t;
    		}
    	}
    	printf("%d %d
    ",ansl,ansr);
    	return 0;
    }
    

      


    By NeighThorn

  • 相关阅读:
    LAMP环境搭建
    Centos系统下Lamp环境的快速搭建(超详细)
    主题:Windows系统服务器磁盘挂载
    云硬盘
    独立IP与共享IP的区别
    网站备案的注意事项
    云主机与vps虚拟主机的区别
    vim 命令大全 / vi 命令大全
    【Linux】Linux中常用操作命令
    Linux Shell常用技巧(一) RE
  • 原文地址:https://www.cnblogs.com/neighthorn/p/6672709.html
Copyright © 2011-2022 走看看