首页 > 代码库 > 8.7联考题解

8.7联考题解

前两题很水啊……没必要开三篇了就扔到一篇里去了。改水题异常艰难,代码量很小然而我又被什么奇怪的东西绊住了。

                                         

Passward

时间限制: 1 Sec  内存限制: 512 MB

题目描述

你来到了一个庙前,庙牌上有一个仅包含小写字母的字符串 s。

传说打开庙门的密码是这个字符串的一个子串 t,并且 t 既是 s 的前缀又是 s 的后缀并且还在 s 的中间位置出现过一次。

如果存在这样的串,请你输出这个串,如有多个满足条件的串,输出最长的那一个。

如果不存在这样的串,输出"Just a legend"(去掉引号)。

输入格式

仅一行,字符串 s。

输出格式

如题所述

样例输入

fixprefixsuffix

样例输出

fix

数据范围

对于 60%的数据, s 的长度<=100

对于 100%的数据, s 的长度<=100000

 

题解

       一拿到卷子……诶,这个题,出题人真当我们没做过cogs一星题吗……就是小暑假集训最后一天大家刷GT考试刷不动了集体去水的那道题嘛。然而由于本蒟蒻字符串的题组做得极慢,所以并没有去水这道题。反正是KMP,没做过现在做也是一样,不过KMP的板子记得不太清楚,还是先往后看。把第二题的暴力分拿到回来看这题,好像我跟wzz讨论过这题应该怎么水,然后用我的o(N)想法没跑过暴力验证……调一调01边界什么的把MP打出来,这题也用不着KMP了,手出几个特殊样例测测都没啥问题,题目好像也没说不能重叠什么的(adadadadada~),反正很多人都A了我也A了就是了。

       中午回cogs去水鱼的感恩,迷之数据范围,开始因为题都没看果断WA,把WA改对了开始T。cogs下面附了一句题源:XXX或51nod1286,然后我欣然去了51nod,发现那道题居然是不允许重叠的。不可以重叠这个要怎么搞啊10^6,不会不会不会……自己挣扎了一个多小时开始问dalao,老白轻描淡写地回答:“hash水之”(不要啊我不想放弃机智的KMP);张司机说:“可以从中间往后枚举啊,不过这个数据范围总有点不祥预感”;ryf说:“等我过了这个题给你讲”。我回来改了改cogs的数组大小发现居然是因为memset超时,然而51nod那题实在没法,打表过第二个点(这居然是唯一一个能卡住毫无根据的依据三倍长度判断的点),去看别人代码。最后在自己的艰苦斗争和ryf大佬的提点下发现这题只有两种方法可以过:hash和扩展KMP。所以我一个不会扩展KMP又不想打hash的人这一下午是为了什么啊喂……自己去看了看扩展KMP的资料,原理还好,要用不还是个板子。处理完我唯一过掉的那题已经快吃晚饭了,我开始改剩下两题……

技术分享
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int sj=100010;
char s[sj];
int len,nt[sj]={0};
bool cx[sj]={0};
void gn()
{
     nt[1]=nt[0]=0;
     for(int i=2,k=0;i<=len;i++)
     {
        while(k&&s[i-1]!=s[k]) k=nt[k];
        if(s[i-1]==s[k]) k++;
        nt[i]=k;
        if(i!=len) cx[nt[i]]=1;
     }
}
int main()
{
    //freopen("t1.txt","r",stdin);
    scanf("%s",s);
    len=strlen(s);
    gn();  
    int jg=nt[len];
    while(jg&&!cx[jg])
      jg=nt[jg];
    if(jg==0) printf("Just a legend");
    else
      for(int i=len-jg;i<len;i++)
        cout<<s[i];
    //while(1);
    return 0;
}
password

 

 

时间限制: 1 Sec  内存限制: 512 MB

 

【背景描述】

一排 N 个数, 第 i 个数是 Ai , 你要找出 K 个不相邻的数, 使得他们的和最大。

请求出这个最大和。

【输入格式】

第一行两个整数 N 和 K。

接下来一行 N 个整数, 第 i 个整数表示 Ai 。

【输出格式】

一行一个整数表示最大和, 请注意答案可能会超过 int 范围

【样例输入】

3 2

4 5 3

【样例输出】

7

【数据范围】

对于 20% 的数据, N, K ≤ 20 。

对于 40% 的数据, N, K ≤ 1000 。

对于 60% 的数据, N, K ≤ 10000 。

对于 100% 的数据, N, K ≤ 100000 , 1 ≤ Ai ≤ 1000000000。

 

【题解】

      这个题目,一看它就很将就……刚开始照例想一遍网络流,建出来图了,Dinic我都打好了,忽然想到网络流怎么限制只选K个数呢……(据某神犇wxh说可以建一个假原点跑费用流,%%%)然后就开始弃疗了,正什么解想暴力。10^4的数据n^2是能过的,然后n^2的dp也很好想,f[i][j]表示到第i位选了j个数,肯定要从f[k][j-1]转移(k<=i-2),拿一个数组记录一下到i-2的最大值,转移很方便。为了优化再看每个i需要计算到多大的j,毕竟i个数里最多选i>>1个。10^4数组差不多能开,但是毕竟想试试运气还是滚动了一下然后把数组开到了10^5,然而事实证明跑不过就是跑不过。60分差不多就算把暴力分都拿到了吧,手出几个样例挑出来几个不是很明显的bug,想想还是有些后怕,果然我该去学对拍了。后来发现第三题一点分拿不到再回来想这道题,感觉正解必须把n甩掉,n*k的效率跑死也过不了,而枚举k又是必须的;尽管如此并没有想到应该怎么做。

       正解是带反悔的贪心,用一些简单的数据结构来支持。用堆维护所有点,每次选中权值最大的点,把它和两侧的点合并为一个点,再把三个原来的点删去,新的点权值设为两侧点-中间被选点权值,方便过后反悔,处理一下前后边界让新的点代替原来的三个点即可。往常都是拿堆水dp,今天居然也有拿dp水堆的时候。晚上改这个题,就是不想用链表也不想用set,感觉两个堆也能做,只要把左右边界指向那个合并后的结构体都可以了。思路一直很清楚,就卡在那么两三个点上过不去,挣扎了一个晚上。有两个点是因为边界限制得太狠,就算已经到了边界也没有特殊处理,而贪心的正确性建立在每次有且只有一个点新被选中的基础上。然后改到晚上睡觉也没改过,宿舍在六楼昨晚蚊子却迷之多,三点多就被闹醒,从蚊帐里抓住了五只不知道什么玩意扔出去,迷迷糊糊一直在想这道题到底什么鬼,想着想着就到点了,被咬得浑身都疼。早上来了之后在结构体重载里加了个如果权值相等按下表排序,莫名其妙就过了。写博客的时候忽然开窍,大概是因为这样一来每个结构体一定会分出确定的顺序,在用垃圾堆删除结构体的时候就能满足在原队列中优先的在垃圾堆中也一定优先,就不会导致用垃圾堆删除结构体不准确了。这样一个小bug折腾了一晚上一早上,把本题的提交正确率拉到极低,过程中万分痛苦,想通了之后却觉得很有意思。

技术分享
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int sj=100010;
int n,k,tp;
struct W
{
    int l,r,size;
    long long w;
    bool operator > (const W &f) const
    {  return w<f.w||(w==f.w&&l<f.l);  }
    bool operator == (const W &f) const
    {  return w==f.w&&l==f.l&&r==f.r;  }
    void in(int x)
    {
        l=r=x;
        scanf("%lld",&w);
    }
}b[sj],temp;
priority_queue<W, vector<W> , greater<W> > a;
priority_queue<W, vector<W> , greater<W> > c;
long long ans;
int main()
{
    scanf("%d%d",&n,&k);
    memset(b,-0xf,sizeof(b));
    b[n+1].r=n+1;
    b[0].l=0;
    for(int i=1;i<=n;i++) b[i].in(i),a.push(b[i]);
    for(int i=1;i<=k;i++)
    {
        while(!c.empty()&&a.top()==c.top())
           a.pop(),c.pop();
        temp=a.top();
        a.pop();
        ans+=temp.w;
        temp.w=-temp.w;
        if(temp.l!=0)
        { 
          temp.w+=b[temp.l-1].w;
          c.push(b[temp.l-1]);
          temp.l=b[temp.l-1].l;
        }
        if(temp.r!=n+1)
        {
          temp.w+=b[temp.r+1].w;
          c.push(b[temp.r+1]);
          temp.r=b[temp.r+1].r;
        }
        b[temp.r]=b[temp.l]=temp;
        a.push(temp);
    }
    printf("%lld",ans);
    return 0;
}
so

 

 

时间限制: 1 Sec  内存限制: 512 MB

 

【题目描述】

Hazel有n本书,编号1为n到 ,叠成一堆。当她每次抽出一本书的时候,上方的书会因重力而下落,这本被取出的书则会被放置在书堆顶。

每次有pi的概率抽取编号为i的书。她每次抽书所消耗的体力与这本书在这堆中是第几本成正比。具体地,抽取堆顶的书所耗费体力值为1 ,抽取第二本耗费体力值为2 ,以此类推。

现在 想知道,在很久很久以后(可以认为几乎是无穷的),她每次抽书所耗费的体力的期望值是多少。

最终的答案显然可以表示成a/b的形式,请输出a*(b^-1)模1e9+7的值。

【输入格式】

第一行一个整数n

接下来n行,每行两个整数ai,bi,代表抽取第i本书的概率是ai/bi

保证所有书的概率和等于1

【输出格式】

输出一行一个整数,代表期望值

【输入样例1】

2

227494 333333

105839 333333

【输出样例1】

432679642

【输入样例2】

10

159073 999999

1493 142857

3422 333333

4945 37037

2227 111111

196276 999999

190882 999999

142721 999999

34858 999999

101914 999999

【输出样例2】

871435606

【数据规模与约定】

对于30%的数据,1<=n<=10。

对于100%的数据,1<=n<=1000,0<=ai<=bi,bi!=0。

 

【题解】

       刚看到这个题,推一推好像不用高斯消元,莫名挺开心,到后来我都不知道我在高兴些什么……有一种很错误的想法,叫做不用高斯消元就要递推,然后坚信一定要从第一本或者最后一本的期望推出其他书的期望,一直抓着期望=概率*权值不放,位置的期望*概率=耗费体力的期望,但是连样例都不知道应该怎么出。感觉要是有哪位dalao做出来了这题也就AK了吧,结果出人意料又情理之中地全场爆零,真想赞美一下出题人。我很弱+题很强=迷之得分率。

       正解是一个简单却不好想的式子,每本书的位置取决于是否有书比它更晚被抽到,所以E[i]=sigma(p[j]/(p[i]+p[j])) , (1<=j<=n&&j!=i),ans=sigma(1+E[i])。虽然给出的和要求的都是分数,但是是在同一个模意义下,所以从一开始就逆元就好了。连续背了很多顿饭之后我终于会写扩展欧几里得了可喜可贺可喜可贺。

技术分享
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int sj=1005;
const ll mod=1000000007;
int n;
ll jg,p[sj],a1,a2,tp;
ll e_gcd(ll n,ll m,ll &sx,ll &sy)
{
     if(m==0)
     {
        sx=1;
        sy=0;
        return n;
     }
     ll ans=e_gcd(m,n%m,sx,sy);
     ll t=sx;
     sx=sy;
     sy=t-n/m*sy;
     return ans;
}
ll ny(ll a,ll b)
{
     ll sx,sy;
     ll gcd=e_gcd(a,b,sx,sy);
     sx*=1/gcd;
     b/=gcd;
     if(b<0) b=-b;
     ll ans=sx%b;
     if(ans<0) ans+=b;
     return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
       scanf("%lld%lld",&a1,&a2);
       a1%=mod;
       p[i]=a1*ny(a2,mod)%mod;
    }
    for(int i=1;i<=n;i++)
    {
       tp=0;
       for(int j=1;j<=n;j++)
         if(i!=j)
           tp=(tp+(p[j]*ny((p[i]+p[j])%mod,mod))%mod)%mod;
       jg=(jg+(p[i]*(1+tp)%mod))%mod;
    }      
    printf("%lld",jg);
    return 0;
}
book

 

8.7联考题解