1. 程式人生 > >8.7聯考題解

8.7聯考題解

中間 應該 費用流 urn prior 毫無 數據結構 期望 考試

前兩題很水啊……沒必要開三篇了就扔到一篇裏去了。改水題異常艱難,代碼量很小然而我又被什麽奇怪的東西絆住了。

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聯考題解