10:26模拟赛

预计得分:\(100+30+30\)
实际得分:\(100+0+20\)
实实际得分:\(100+30+20\)




NOIP模拟赛

Reverse

题目描述

  0比1小,所以1个串中如果0全在1前面,这个串就比较和谐。对于1个只包含
0和1的串,你每次可以将若干个0变成1,或者将若干个1变成0。那么,最少需要变多
少次才能把保证所有的0在1前⾯呢?

输入格式

一个01串。

输出格式

一个数即答案。

数据范围与约定

对于\(40\%\)的数据 ,\(len\leq 20\)
对于\(70\%\)的数据,\(len\leq 1000\)
对于\(100\%\)的数据,\(len\leq 10^5\)



做法:枚举0和1交界的位置
利用前缀和统计答案。
因为太水所以2分钟就做完了第一题

#include<iostream>
#include<cstdio>
#define N 500005
using namespace std;

void read(int & s){
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar());
    for(s=0;isdigit(ch);s=s*10+ch-'0',ch=getchar());
}

int n;
int s[N],q[N];

int main(){
    freopen("reverse.in","r",stdin);
    freopen("reverse.out","w",stdout);
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar());
    for(;isdigit(ch);s[++n]=ch-'0',ch=getchar());
    for(int i=1;i<=n;++i)
        q[i]=q[i-1]+s[i];
    int ans=0x3f3f3f3f;
    int qqq=0;
    for(int i=0;i<=n;++i){
        qqq=0;
        qqq+=q[i]+n-i-q[n]+q[i];
        ans=min(qqq,ans);
    }
    printf("%d",ans);
    return 0;
}





Number

题目描述

  给定 ,求有多少数对\(<x,y>\)满足:
\(x,y\in [1,n],x<y\)
\(x,y\)中出现的\([0,9]\)的数码种类相同。

输入格式1个整数 。

输出格式输出1个数即答案。

数据范围与约定

对于\(30\%\)的数据,\(n\leq 10^3\)
对于\(100\%\)的数据,\(n\leq 10^7\)



做法:打表没打完而且代码只允许10KB而且只能过30%的数据
而且暴力都能打30分
所以这题打表失败了
本来想用组合数的相关内容做
但是不好维护而且代码应该会巨长巨恶心
就没写



正解思路:
首先统计出每个数其所有拥有数位的信息
因为产生<a,b>关系的数\(A,B\)其拥有的数码相同
这个可以通过下述方式达到
\(S_n\)表示\(n\)的数位信息
\[S_n=\sum_{i=0}^{9} 2^i(n/10^k\equiv i\pmod{10})\]
则统计出所有数位相同的组数通过排列组合得出即可
我们设某一数位\(S\)的数量为\(sum_S\),则易知\(sum_S=C_{sum_S}^2\)
因为每两个拥有数位S相同的数\(a_1,a_2\)
a_1\not =a_2
所以对于任意\(a_i,a_j\in S\),都有\(a_i\not =a_j\)
所以总共的\(<a_i,a_j>\)的个数为\(C_{sum_S}^2\)
所以最终答案为\(\sum_{i=1}^n C_{s_{S_i}}^2*s_{S_i}\)
其中n为所以不同数位的个数




Wave

题目描述

  给定一个长为\(n\)的数列,试求一个最长的不稳定波动子序列满足任意偶数项的
值不小于其相邻两项的值,且相邻两项的差不大于\(k\)

输入格式

输入第一行两个正整数 ;
第 n行 个非负整数描述这个数列。

输出格式

输出一个整数即答案。

数据范围及约定

对于\(30\%\)的数据,\(n\leq 10^3\)
对于\(70\%\)的数据,\(n\leq 10^5\)
对于\(100\%\)的数据,\(n\leq 2*10^6\), 数列中的数不超过\(2^{31}-1\)



做法:\(O(n^2)dp\)
能过30的数据没问题
但是说好的\(30\%\)数据\(10^3\)
呢???怎么变\(10^5\)了……
这个做法可以用线段树优化
具体是用权值线段树维护
正解是贪心
考场上想到贪心
然后开始YY
有一段单增区间\([l,r]\)
答案应该存在于\([l,r]\)的两端才对
然后脑残的我竟然接着想如果\([l,r]\)中存在一个很低的点
亲手把我自己的假设抛弃了
2333
然后就抛弃了正解做法
考后写了两份代码

#include<iostream>
#include<cstdio>
#define N 2000007
#define inf 0x7fffffff
using namespace std;

int s[N];
int que[N];

inline int read() {
    int x=0;
    char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c<='9'&&c>='0')x=x*10+c-'0',c=getchar();return x;
}

int main(){
    int n=read();
    int k=read();
    que[0]=inf;
    int now=0;
    for(int i=1;i<=n;++i)s[i]=read();
    for(int i=1;i<=n;++i){
        if(now&1){
            if(s[i]>=que[now]+k)que[++now]=s[i];
            if(s[i]<que[now])que[now]=s[i];
        }
        else {
            if(s[i]+k<=que[now])que[++now]=s[i];
            if(s[i]>que[now])que[now]=s[i];
        }
    }
    printf("%d",now);
    return 0;
}
#include<iostream>
#include<cstdio>
#define N 2000007
#define inf 0x7fffffff
using namespace std;

int n,k;
int s[N];
int que[N];

inline void read(int & s){
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar());
    for(s=0;isdigit(ch);s=s*10+ch-'0',ch=getchar());
}

int main(){
    read(n);read(k);
    int now=1;
    que[0]=inf;
    s[n+1]=inf;
    for(int i=1;i<=n;++i)read(s[i]);
    for(int i=1;i<=n;++i){
        if(now&1){
            while(s[i]+k>que[now-1]){
                if(s[i]>que[now-1])que[now-1]=s[i];
                i++;
            }
            while(s[i+1]<s[i]&&i<n)i++;
            if(que[now-1]<s[i]+k||i==n+1){
                break;
            }
            que[now]=s[i];++now;    
        }
        else {
            while(s[i]<que[now-1]+k){
                if(s[i]<que[now-1])que[now-1]=s[i];
                i++;
            }
            while(s[i+1]>s[i]&&i<n)i++;
            if(s[i]<que[now-1]+k||i==n+1){
                break;
            }
            que[now]=s[i];++now;
        }
    }
    int ans=0;
    printf("%d",now-1);
    return 0;
}

转载于:https://www.cnblogs.com/qdscwyy/p/7737824.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值