[NEFU ACM大一暑假集训 解题报告]尺取法

Posted 鱼竿钓鱼干

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[NEFU ACM大一暑假集训 解题报告]尺取法相关的知识,希望对你有一定的参考价值。

[NEFU ACM大一暑假集训 解题报告]尺取法

前四题为例题,学长讲过了,直接贴代码了。

题谱

在这里插入图片描述

题目

A - Subsequence

求总和>=s的最短区间

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<cstring>
using namespace std;

typedef long long LL;
#define endl '\\n'

int T;
const int N=1e5+10;
int a[N],sum[N];
int n,s;
bool check(int i,int j){
    return (sum[i]-sum[j-1])>=s;
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&s);
        int ans=1e9+7;
        memset(sum,0,sizeof sum);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            sum[i]=sum[i-1]+a[i];
        }
        for(int i=1,j=1;i<=n;i++){//j左端点,i右端点
            while(j<=i&&check(i,j))j++;//满足条件就要不断缩小区间
            if(check(i,j-1))ans=min(ans,i-(j-1)+1);
        }
        if(ans==1e9+7)printf("0\\n");
        else printf("%d\\n",ans);
    }
    return 0;
}

B - Jessica’s Reading Problem

求包含所有种类元素的最短区间

#include<cstdio>
#include<set>
#include<map>
using namespace std;

typedef long long LL;
#define endl '\\n'

const int N=1000005;
int p,st,en,num,sum;
int a[N];
set<int>s;
map<int,int>mp;

int main(){
    scanf("%d",&p);
    for(int i=0;i<p;i++){
        scanf("%d",a+i);
        s.insert(a[i]);
    }
    num=s.size();
    int ans=1e9+7;
    while(1){
        while(en<p&&sum<num){       //扩展区间延长右端点
            if(mp[a[en]]==0)sum++;
            mp[a[en]]++;
            en++;
        }
        if(sum<num)break;           //处理没有任何满足条件的区间
        ans=min(ans,en-st);         //更新答案
        if(mp[a[st]]-1==0)sum--;    //缩小区间缩短左端点
        mp[a[st]]--;
        st++;
    }
    
    printf("%d\\n",ans);
    return 0;
}

C - Bound Found

这题主要和I题做区分,两者前缀和的都是非单调的,但是这题的话是前缀和的绝对值与其他的差值,所以可以排序后直接利用其前缀和的单调性。也就说说点对(i,j)和点对(j,i)的对于这题而言是完全相同的,只需保证输出答案的时候点对符合顺序即可

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;

typedef long long LL;
#define endl '\\n'

const int N=100005;
int n,m,t,ans,l,r;
struct Seg{
    int sum;int id;
}seg[N];
int a[N];
bool cmp(Seg A,Seg B){return A.sum<B.sum;};
int main(){
    while(~scanf("%d%d",&n,&m),n||m){
        seg[0].id=0;seg[0].sum=0;
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            seg[i].sum=seg[i-1].sum+a[i];
            seg[i].id=i;
        }
        sort(seg,seg+1+n,cmp);
        while(m--){
            scanf("%d",&t);
            int st=0,ed=1,tmp=1e9+7;
            while(st<=n&&ed<=n){
                int seg_sum=seg[ed].sum-seg[st].sum;
                if(abs(seg_sum-t)<tmp){
                    tmp=abs(seg_sum-t);
                    ans=seg_sum;
                    l=seg[st].id,r=seg[ed].id;
                }
                if(seg_sum>t)st++;
                else if(seg_sum<t)ed++;
                else break;
                if(st==ed)ed++;
            }
            if(r<l)swap(r,l);
            printf("%d %d %d\\n",ans,l+1,r);
        }
    }
    return 0;
}

其他写法

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;

typedef long long LL;
#define endl '\\n'

const int N=100005;
int n,m,t,ans,l,r;
struct Seg{
    int sum;int id;
}seg[N];
int a[N];
bool cmp(Seg A,Seg B){return A.sum<B.sum;};
int main(){
    while(~scanf("%d%d",&n,&m),n||m){
        seg[0].id=0;seg[0].sum=0;
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            seg[i].sum=seg[i-1].sum+a[i];
            seg[i].id=i;
        }
        sort(seg,seg+1+n,cmp);
        while(m--){
            scanf("%d",&t);
            /*暴力
            int tmp=1e9+7,l=0,r=0,ans=0;
            for(int i=0;i<=n;i++){
                for(int j=0;j<=n;j++){
                    int seg_sum=abs(seg[j].sum-seg[i].sum);
                    if(abs(seg_sum-t)<tmp&&seg[j].id>seg[i].id){
                        tmp=abs(seg_sum-t);
                        ans=seg_sum;
                        l=seg[i].id+1,r=seg[j].id;
                    }
                }
            }*/

            int tmp=1e9+7,left=0,right=1;//left不能和right相等
            while(right<=n){
                int seg_sum=seg[right].sum-seg[left].sum;
                if(abs(seg_sum-t)<tmp){
                    tmp=abs(seg_sum-t);
                    l=seg[left].id,r=seg[right].id,ans=seg_sum;
                }
                if(seg_sum>t)left++;
                else if(seg_sum<t)right++;
                else break;
                if(left==right)right++;
            }
            if(l>r)swap(l,r);
            printf("%d %d %d\\n",ans,l+1,r);
        }
    }
    return 0;
}

D - Sum of Consecutive Prime Numbers

求n表示为连续素数和的方案数

#include<cstdio>
#include<cstring>
using namespace std;

typedef long long LL;
#define endl '\\n'
const int N=10010;
int primes[N],cnt;
bool st[N];
void get_primes(int n){
    memset(st,0,sizeof st);
    st[0]=st[1]=1;
    for(int i=2;i<=n;i++){
        if(!st[i])primes[++cnt]=i;
        for(int j=1;primes[j]<=n/i;j++){
            st[primes[j]*i]=true;
            if(i%primes[j]==0)break;
        }
    }
}
int n,ans,be,en,sum;
int main(){
    get_primes(N);
    while(~scanf("%d",&n),n){
        be=en=1;ans=sum=0;
        while(1){
            if(sum==n)ans++;//更新答案
            if(sum>=n){//收缩
                sum-=primes[be];
                be++;
            }
            else{
                if(primes[en]<=n){//扩展
                    sum+=primes[en];
                    en++;
                }
                else break;//超出范围停止扩展结束
            }
        }
        printf("%d\\n",ans);
    }
    return 0;
}

另外写法(和G题统一)

#include<cstdio>
#include<cstring>
using namespace std;

typedef long long LL;
#define endl '\\n'
const int N=10010;
int primes[N],cnt;
bool st[N];
void get_primes(int n){
    memset(st,0,sizeof st);
    st[0]=st[1]=1;
    for(int i=2;i<=n;i++){
        if(!st[i])primes[++cnt]=i;
        for(int j=1;primes[j]<=n/i;j++){
            st[primes[j]*i]=true;
            if(i%primes[j]==0)break;
        }
    }
}
int n,ans,be,en,sum;
int main(){
    get_primes(N);
    while(~scanf("%d",&n),n){
        be=1,en=1;ans=sum=0;
        for(;;){
            while(primes[en]<=n&&sum<n){	//扩展右区间
                sum+=primes[en];
                en++;
            }
            if(sum<n)break;					//处理没有任何满足的区间
            if(sum==n)ans++;				//更新答案
            sum-=primes[be];				//缩小左区间
            be++;
        }
        
        printf("%d\\n",ans);
    }
    return 0;
}

E - NanoApe Loves Sequence Ⅱ

题目问满足 区间内第k大的数>=m 的区间个数
第k大的数>=m,那么说明区间内至少有k个数>=m.
我们预处理把序列中>=m的标记为1,其余标记为0,然后求标记的前缀和数组。
接下来问题就变成了有多少个区间满足[l,r]的区间和>=k,看第一个例题A即可求解

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef pair<int,int>PII;
#define endl '\\n'

int T;
const int N=200005;
LL a[N],s[N];
LL n,m,k;
bool check(int i,int j){
    return s[i]-s[j-1]>=k;
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%lld%lld%lld",&n,&m,&k);
        memset(s,0,sizeof s);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]以上是关于[NEFU ACM大一暑假集训 解题报告]尺取法的主要内容,如果未能解决你的问题,请参考以下文章

[NEFU ACM大一暑假集训 解题报告]前缀和与差分

[NEFU ACM] 2020级暑期训练 解题报告

HDU 6103 Kirinriki 枚举长度 尺取法

河南理工大学算法协会暑期集训积分赛网络同步赛-Numbers of interval-尺取法

2018年暑假ACM个人训练题9(动态规划)解题报告

HDU 6205(尺取法)2017 ACM/ICPC Asia Regional Shenyang Online