NOIP2017 赛前模拟 7.24

本次考试三道题据出题人的意思都是模板题,T1单调队列模板,T2数位DP模板,T3线段树模板,但由于前两个模板都不熟悉,考试的时候不敢写,线段树也写炸了,最后只有50分。

T1:
题目描述:
在一个包含 n 个元素的数组上,有一个长度为 k 的窗户在从左向右滑动。窗户每滑动到一个位置,我们都可以看到 k 个元素在窗户中。如下的例子所示,假设数组为 [1 3 -1 -3 5 3 6 7],而 k 等于 3 :
这里写图片描述

输入格式
输入的第一行包括两个整数 n,k ,n 表示数组的长度,k 表示窗户的长度。
接下来一行包括 n 个整数,表示这个 n 个元素的数组。

输出格式
输出包含两行,每行包括 n-k+1 个整数。
第一行表示窗户从左到右滑动过程中的最小值。
第二行表示窗户从左到右滑动过程中的最大值。

备注
【数据范围】
对于 100% 的数据,3<=n<=1000000,1<=k<=n,数组中的每个元素均在 int 范围内。

题意:每次选k个数,统计最大值和最小值。所以在我无法打出正解时,想到线段树的区间查询,勉强得了50分,DZY也是线段树但A了,我也没办法。

正解:维护两个单调队列,里面存的是最小值和最大值的下标,一个是维护最小值,一个是维护最大值。每次打新的数压入队尾,先考虑维护最小值的队列,如果前面的数比他还大,说明这些数都无法对答案产生共贡献,就直接r- - (队尾的编号),就将其往前移,最大值也是如此

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<cctype>
using namespace std;
const int N =1e6+5;
int n,k,q1[N],q2[N],a[N],ans1[N],ans2[N],buf[1000];
//---------------------
inline int Readint()
{
    int i=0,f=1; char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-') f=-1,ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<1)+(i<<3)+ch-'0';
    return i*f;
}
//---------------------
inline void W(int x) //这道题因为数据比较大,输出优化
{
    if(!x){putchar('0');return;}
    if(x<0){putchar('-');x=-x;}
    while(x)buf[++buf[0]]=x%10,x/=10;
    while(buf[0])putchar('0'+buf[buf[0]--]);
}
//---------------------
int main()
{
    freopen("window.in","r",stdin);
    //freopen("window.out","w",stdout);

    n=Readint(),k=Readint();
    for(int i=1;i<=n;i++) a[i]=Readint();
    int l1=0,l2=0,r1=1,r2=1;
    for(int i=1;i<=n;i++){
        while(l1<=r1 && q1[l1]<=i-k) l1++; //最小值的队列
        while(l2<=r2 && q2[l2]<=i-k) l2++; //最大值的队列 
        while(l1<=r1 && a[i]<=a[q1[r1]]) r1--; //比他小就一直减
        q1[++r1]=i;
        while(l2<=r2 && a[i]>=a[q2[r2]]) r2--; //比他大就一直减
        q2[++r2]=i;
        ans1[i]=a[q1[l1]];
        ans2[i]=a[q2[l2]];
    }
    for(int i=k;i<=n;i++) W(ans1[i]),putchar(' ');
    cout<<endl;
    for(int i=k;i<=n;i++) W(ans2[i]),putchar(' ');
    return 0;
}

T2: 裸的数位DP
题目描述:
CLC NOIP2015 惨跪,他依稀记得他的准考证号是 37(其实是假的),现在CLC又将要面临一场比赛,他希望准考证号不出现 37(连续),同时他又十分讨厌 4 ,所以也不希望 4 出现在准考证号中。现在他想知道在 A 和 B 之间有多少合法的准考证号

数位Dp 不解释了

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<iomanip>
#include<iostream>
#include<cmath>
#include<ctime>
#include<cctype>
using namespace std;
int x,y,a[20],dp[20][2];
//---------------------
inline int Readint()
{
    int i=0,f=1; char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-') f=-1,ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<1)+(i<<3)+ch-'0';
    return i*f;
}
//---------------------
/*
pos是当前枚举到的位数,pre为前一位的数,为了判断是否为3,sta为当前的状态,limit是判断最大能不能枚举到9;
*/
//---------------------
inline int dfs(int pos,int pre,int sta,int limit) 
{
    if(pos==-1) return 1;
    if(!limit && dp[pos][sta]!=-1) return dp[pos][sta];
    int up= limit ? a[pos] : 9;
    int tmp=0;
    for(int i=0;i<=up;i++){
        if(pre==3 && i==7) continue;
        if(i==4) continue;
        tmp+=dfs(pos-1,i,i==3,limit && i==a[pos]);
    }
    if(!limit) dp[pos][sta]=tmp;
    return tmp;
}
//---------------------
inline int solve(int x)
{
    int pos=0;
    while(x) //算出每一位数
    {
        a[pos++]=x%10;
        x=x/10;
    }
    return dfs(pos-1,-1,0,true);
}
//---------------------
int main()
{
    x=Readint(),y=Readint();
    memset(dp,-1,sizeof(dp));
    cout<<solve(y)-solve(x-1); //统计[0,y]和[0,x-1]的值相减
    return 0;
}

T3:
题目描述:
万恶的大头又出现了!他正在玩一个智障游戏:打怪兽。

现在大头的屏幕上出现了一排怪兽,每只怪兽头上有一个血条,每次大头可以选择一个区间进行攻击,攻击值为 K ,这个区间中血量小于 K 的怪兽都会被大头无情地干掉,当然怪兽不会坐以待毙,对于一个区间的怪兽,他们会在某个时刻血量同时加 X 。

头头虽然很大,但是 IQ 并不高,在座的各位选手都不知道比他高到哪里去了。这个时候大头使出了大招——作弊器,然而大头的作弊器并不高级只能将选择的区间内血量为 7 的倍数的怪兽干掉,问:他能干掉多少怪兽?

输入格式
第一行一个正整数 n ;
接下来 n 行 n 个整数;
再接下来一个正整数 Q ,表示操作的个数;
接下来 Q 行每行若干个整数。如果第一个数是 add ,后接 3 个正整数 a,b,X,表示在区间 [a,b] 内每个数增加 X,如果是 count,表示统计区间 [a,b] 能被 7 整除的个数。

正解:线段树打标记,统计每个区间内数值与7取模和 为 0,1,2,3,4,5,6 的个数 ,最后只用暑促余数为0的个数即可

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<cctype>
using namespace std;
const int N = 1e5+5;
int a[N],n,m,x,y,z,num;
int sum[N<<2][10],add[N<<2],tmp[10];
char s[10];
//---------------------
inline int Readint()
{
    int i=0,f=1; char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-') f=-1,ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<1)+(i<<3)+ch-'0';
    return i*f;
}
//---------------------
inline void update(int root)
{
    for(int i=0;i<=6;i++)
      sum[root][i]=sum[root<<1][i]+sum[root<<1|1][i];
}
//---------------------
inline void mode(int k,int x)
{
    memset(tmp,0,sizeof(tmp));
    for(int i=0;i<=6;i++) tmp[i]=sum[k][i];
    for(int i=0;i<=6;i++) sum[k][(i+x)%7]=tmp[i];
}
//---------------------
void Pushdown(int k)
{
    if(add[k])
    {
        add[k<<1]=(add[k<<1]+add[k])%7,mode(k<<1,add[k]);
        add[k<<1|1]=(add[k<<1|1]+add[k])%7,mode(k<<1|1,add[k]);
        add[k]=0;
    }
}
//---------------------
void build(int k,int l,int r)
{
    if(l==r)
    {
        sum[k][a[l]]++;
        return;
    }
    int mid=l+r>>1;
    build(k<<1,l,mid),build(k<<1|1,mid+1,r);
    update(k);
}
//---------------------
void modify(int k,int l,int r,int x,int y,int v)
{
    if(x<=l&&r<=y)
    {
        memset(tmp,0,sizeof(tmp));
        mode(k,v);
        add[k]=(add[k]+v)%7;
        return;
    }
    Pushdown(k);
    int mid=l+r>>1;
    if(y<=mid)modify(k<<1,l,mid,x,y,v);
    else if(x>mid)modify(k<<1|1,mid+1,r,x,y,v);
    else modify(k<<1,l,mid,x,mid,v),modify(k<<1|1,mid+1,r,mid+1,y,v);
    update(k);
}
//---------------------
inline int Query(int k,int l,int r,int x,int y)
{
    if(x<=l&&r<=y) return sum[k][0];
    Pushdown(k);
    int mid=(l+r)>>1;
    if(y<=mid)  return Query(k<<1,l,mid,x,y);
    else if(x>mid) return Query(k<<1|1,mid+1,r,x,y);
    else return Query(k<<1,l,mid,x,y)+Query(k<<1|1,mid+1,r,x,y);
}
//---------------------
int main()
{
    freopen("seg.in","r",stdin);
    //freopen("seg.out","w",stdout);

    n=Readint();
    for(int i=1;i<=n;i++) a[i]=Readint()%7;
    build(1,1,n);
    m=Readint();
    for(int i=1;i<=m;i++){
        scanf("%s",s);
        if(s[0]=='a'){
            x=Readint(),y=Readint(),z=Readint()%7;
            modify(1,1,n,x,y,z);
        }
        if(s[0]=='c') {
            x=Readint(),y=Readint();
            cout<<Query(1,1,n,x,y)<<endl;
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值