NOIP模拟赛2017.9.11 考试心得+总结

这里写图片描述
世界真的很大
考虑很多次试了,这还是第一次为考试写博客,之前觉得没什么好处,但是想一想有时考试还是很有营养的
包括做题时的思路,考试时的心态,这些都是比较重要的,很多时候需要记录的不光是题解而已
一共三道题,考试时总分235
看题先:

1.Passward

你来到了一个庙前,庙牌上有一个仅包含小写字母的字符串 s。
传说打开庙门的密码是这个字符串的一个子串 t,并且 t 既是 s 的前缀又是 s 的后缀并且还在 s 的中间位置出现过一次。
如果存在这样的串,请你输出这个串,如有多个满足条件的串,输出最长的那一个。
如果不存在这样的串,输出"Just a legend"(去掉引号)。
输入格式:
仅一行,字符串 s。
输出格式:
如题所述
样例输入
fixprefixsuffix
样例输出:
fix
数据范围:
对于 60%的数据, s 的长度<=100
对于 100%的数据, s 的长度<=100000 

一开始做题的时候没有看到还需要中间出现,以为就是KMP求一下nxt数组就行了,1分钟写完就跑了
然后检查的时候才发现没对,马上回头来想,当时吓得心都飞了,以为其实是一道难题
然后稍微一看,可以先跑一边KMP求NXT,算出最长的长度,再去二分这个长度,每次check的时候再去判断一下这个串有没有在整个串里面出现3次以上,因为头一次,尾一次,中间至少一次
就85分,有三个点wa了
下来一想,二分其实是错的,到不如说得了85分才觉得奇怪。。。
赶快改了一个比较暴力但是仔细想想其实是对的的方法A了。就是倒着挨个挨个的去check。这个就需要利用一下这道题本身的性质了

这次真是有惊无险,一个错误的方法就有85分,但是这次之后一定要长记性,要记得去验证代码的正确性

完整代码:

#include<stdio.h>
#include<string.h>

int n,m,nxt[100010];
char t[100010],ss[100010];

void calnxt()
{
    n=strlen(ss),nxt[0]=-1;
    int i=0,j=-1;
    while(i<n)
    {
        if(j==-1 || ss[i]==ss[j])
        {
            i++,j++;
            nxt[i]=j;
        }
        else j=nxt[j];
    }
}

int KMP()
{
    int i=0,j=0,bns=0;
    while(i<n)
    {
        if(j==-1||ss[i]==t[j])
        {
            i++,j++;
            if(j==m)
            {
                bns++;
                j=nxt[1];
            }
        }
        else
        j=nxt[j];
    }
    return bns;
}

bool check(int len)
{
    memset(t,0,sizeof(t));
    for(int i=0;i<len;i++)
        if(ss[i]!=ss[n-len+i]) return false ;
    for(int i=0;i<len;i++)
        t[i]=ss[i];
    m=len;
    if(KMP()>=3) return true;
    return false ;
}

int main()
{
    freopen("passward.in","r",stdin);
    freopen("passward.out","w",stdout);
    scanf("%s",ss);
    calnxt();
    if(nxt[n]==0)
    {
        printf("Just a legend\n");
        return 0;
    }
    int lf=1,rg=nxt[n],ans=-1;
    for(int i=rg;i>=1;i--)
        if(check(i))
        {
            ans=i;
            break ;
        }
    if(ans==-1)
    {
        printf("Just a legend\n");
        return 0;
    }
    for(int i=0;i<ans;i++)
        printf("%c",ss[i]);
    return 0;
}

2.SO

就
【背景描述】
一排 N 个数, 第 i 个数是 Ai , 你要找出 K 个不相邻的数, 使得他们的和最大。
请求出这个最大和。
【输入格式】
第一行两个整数 NK。
接下来一行 N 个整数, 第 i 个整数表示 Ai 。
【输出格式】
一行一个整数表示最大和, 请注意答案可能会超过 int 范围
【样例输入】
3 2
4 5 3
【样例输出】
7
【数据范围】
对于 20% 的数据, N, K20 。
对于 40% 的数据, N, K1000 。
对于 60% 的数据, N, K10000 。
对于 100% 的数据, N, K1000001 ≤ Ai ≤ 1000000000

老实说这道题我没做出来,到现在也没有调出来,但还是总结一下我的60分DP做法
如果要看题解的话,麻烦去搜一下,应该还有人写了的
老实说DP的思路还是很好想,一个简单的二维DP,f[j][i] 表示前i个选j个的最大值
转移方程就是f[j][i]= max( f[j][i-1] , f[j-1][i-2]) ,以此保证不相邻
然后由于60分的数据范围数组可能开不下,于是当即决定换成滚动的
时间复杂度O(1/4 NK)

完整代码(60分):

#include<stdio.h>
#include<iostream> 
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long dnt;

int cur=1,pre=0;
dnt n,K,a[100010],f[2][100010];

int main()
{
    freopen("so.in","r",stdin);
    freopen("so.out","w",stdout);
    cin >> n >> K;
    for(int i=1;i<=n;i++) cin >> a[i];
    f[cur][1]=a[1];
    for(int i=2;i<=n;i++)
        f[cur][i]=max(f[cur][i-1],a[i]);
    for(int j=2;j<=K;j++)
    {
        swap(cur,pre);
        memset(f[cur],0,sizeof(f[cur]));
        for(int i=2*j-1;i<=n;i++)
            f[cur][i]=max(f[cur][i-1],f[pre][i-2]+a[i]);
    }
    cout << f[cur][n] << endl;
    return 0;
}

3.BOOK

书
Hazel有n本书,编号1为n到 ,叠成一堆。当她每次抽出一本书的时候,上方的书会因重力而下落,这本被取出的书则会被放置在书堆顶。
每次有pi的概率抽取编号为i的书。她每次抽书所消耗的体力与这本书在这堆中是第几本成正比。具体地,抽取堆顶的书所耗费体力值为1 ,抽取第二本耗费体力值为2 ,以此类推。
现在 想知道,在很久很久以后(可以认为几乎是无穷的),她每次抽书所耗费的体力的期望值是多少。
最终的答案显然可以表示成a/b的形式,请输出a*(b^-1)模1e9+7的值。
【输入格式】
第一行一个整数n
接下来n行,每行两个整数ai,bi,代表抽取第i本书的概率是ai/bi
保证所有书的概率和等于1
【输出格式】
输出一行一个整数,代表期望值
【输入样例12
227494 333333
105839 333333
【输出样例1432679642
【输入样例210
159073 999999
1493 142857
3422 333333
4945 37037
2227 111111
196276 999999
190882 999999
142721 999999
34858 999999
101914 999999
【输出样例2871435606
【数据规模与约定】
对于30%的数据,1<=n<=10。
对于100%的数据,1<=n<=1000,0<=ai<=bi,bi!=0。

这道题虽然最后写起来感觉很玄学,但是确实是能够一笔一划地手推过来的,期望方程。。几乎等于没有
能把这道题在考试时写出来我还是很高兴的
首先求的是总的体力值期望,就是累加每本书的期望体力值乘以每本书的概率
概率在任何时候都相同,且已经给出来了,那我们其实想要知道的就是每本书的期望体力值了
考虑每本书的期望体力值等价于每本书在书堆里的期望位置,等价于在他头上的书的数量的期望值+1
在他头上的书的数量的期望等价于有多少本书期望在他头上
就相当于累加每本书在它头上的期望,这个等于1 * 这本书在他头上的概率
华聚划算1他头上所有书的数量的期望值就等价于所有书在他头上的概率的累加
现在我们想要知道的就是每本书在每本书头上的概率是多少
考虑书i和书j,假设一共i被抽中了x次,j被抽中y次,由于抽了无数次了,我们可以近似的把x/y等价于pi/pj
等价于i被抽中了pi次,j被抽中了pj次
j在i上面的概率是多少,由于抽了无数次了,我们有充足的理由相信整个书堆已经被完全打乱了(如果不是无限次我就不会了)
那么最后j是不是在i的上面就和一开始的书堆完全没有关系了,j在i的上面当且仅当j比i晚抽中
j和i一共被抽中了pi+pj次,我们想要知道的,就是最后一次是j的概率,很显然是pj/(pi+pj)
完美

总结一下很多时候不管是方程也好,代码也好,狠下心来去写其实会发现也并没有那么难,不要害怕
要有勇于用代码实现想法的勇气

完整代码(100分):

#include<stdio.h>
#include<iostream>
using namespace std;
typedef long long dnt;

const dnt mod=1e9+7;

int n;
dnt ans=0,p[100010];

dnt exgcd( dnt a, dnt b, dnt &x, dnt &y ) 
{
    if(b==0) 
    {
        x=1,y=0;
        return a;
    }
    dnt x0,y0;
    dnt cd=exgcd(b,a%b,x0,y0);
    x=y0;
    y=x0-(a/b)*y0;
    return cd;
}

dnt inverse( dnt a ) 
{
    dnt x, y;
    exgcd(a,mod,x,y);
    return (x%mod+mod)%mod;
}

int main()
{
    freopen("book.in","r",stdin);
    freopen("book.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        dnt a,b;
        cin >> a >> b;
        p[i]=a*inverse(b)%mod;
    }
    for(int i=1;i<=n;i++)
    {
        dnt tmp=1;
        for(int j=1;j<=n;j++)
        {
            if(j==i) continue ;
            tmp=(tmp+p[j]*inverse(p[i]+p[j])%mod)%mod;
        }
        ans=(ans+p[i]*tmp%mod)%mod;
    }
    cout << ans << endl;
    return 0;
}

嗯,就是这样

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值