zoukankan      html  css  js  c++  java
  • 联赛模拟测试17 A. 简单的区间 启发式合并

    题目描述

    分析

    我们要找的是一段区间的和减去该区间的最大值能否被 (k) 整除
    那么对于一段区间,我们可以先找出区间中的最大值
    然后枚举最大值左边的后缀与最大值右边的前缀之和是否能被 (k) 整除
    显然暴力枚举肯定会超时
    所以我们可以用启发式合并的思想,只枚举长度较小的那一半,而在某种数据结构中查询另一半对应的值
    查询的过程可以用主席树,但是常数巨大
    其实我们可以对于每一个 (\%k) 后的前缀和开一个 (vector)
    (vector) 中存放该值出现的位置
    然后大力二分即可,复杂度和主席树相同
    注意具体查的值要推一下式子

    代码

    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    #define rg register
    inline int read(){
    	rg int x=0,fh=1;
    	rg char ch=getchar();
    	while(ch<'0' || ch>'9'){
    		if(ch=='-') fh=-1;
    		ch=getchar();
    	}
    	while(ch>='0' && ch<='9'){
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return x*fh;
    }
    const int maxn=3e5+5,maxm=1e6+5;
    int n,k,a[maxn],sum[maxn],wz[maxn][22],ans,cnt[maxm],lg[maxn],b[maxn];
    std::vector<int> g[maxm];
    int cx(int l,int r){
    	rg int k=lg[r-l+1];
    	if(b[wz[l][k]]<b[wz[r-(1<<k)+1][k]]) return wz[r-(1<<k)+1][k];
    	else return wz[l][k];
    }
    int js(int id,int l,int r){
    	if(l>r || g[id].size()==0 || g[id][g[id].size()-1]<l) return 0;
    	return std::upper_bound(g[id].begin(),g[id].end(),r)-std::lower_bound(g[id].begin(),g[id].end(),l);
    }
    void solve(int l,int mids,int r){
    	if(l>mids || r<mids) return;
    	solve(l,cx(l,mids-1),mids-1);
    	solve(mids+1,cx(mids+1,r),r);
    	if(mids-l<r-mids){
    		for(rg int i=l;i<=mids;i++){
    			rg int now=(sum[i-1]+a[mids])%k;
    			ans+=js(now,mids+1,r);
    		}
    		ans+=js(sum[mids-1],l-1,mids-2);
    	} else {
    		for(rg int i=mids;i<=r;i++){
    			rg int now=(sum[i]-a[mids]+k)%k;
    			ans+=js(now,l-1,mids-2);
    		}
    		ans+=js(sum[mids],mids+1,r);
    	}
    }
    int main(){
    	n=read(),k=read();
    	for(rg int i=1;i<=n;i++){
    		a[i]=read();
    		b[i]=a[i];
    		a[i]%=k;
    		wz[i][0]=i;
    	}
    	for(rg int i=1;i<=n;i++){
    		sum[i]=sum[i-1]+a[i];
    		if(sum[i]>=k) sum[i]-=k;
    		g[sum[i]].push_back(i);
    	}
    	for(rg int i=2;i<=n;i++){
    		lg[i]=lg[i/2]+1;
    	}
    	for(rg int j=1;j<=20;j++){
    		for(rg int i=1;i+(1<<j)-1<=n;i++){
    			if(b[wz[i][j-1]]>b[wz[i+(1<<(j-1))][j-1]]){
    				wz[i][j]=wz[i][j-1];
    			} else {
    				wz[i][j]=wz[i+(1<<(j-1))][j-1];
    			}
    		}
    	}
    	g[0].push_back(0);
    	for(rg int i=0;i<k;i++){
    		std::sort(g[i].begin(),g[i].end());
    	}
    	rg int be=cx(1,n);
    	solve(1,be,n);
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    数组
    分支.顺序结构
    博客作业-查找
    DS博客作业-图
    DS 数据结构-树
    数据结构-栈,队列
    博客作业05-指针
    C语言博客作业04-数组
    C语言博客作业03——函数
    c语言博客作业02-循环结构
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/13828067.html
Copyright © 2011-2022 走看看