1. 程式人生 > >Codeforces Educational Codeforces Round 48

Codeforces Educational Codeforces Round 48

模擬題真實難..嘔

A.Death Note

題意,給你一本書和一個m,每頁上可以寫m個名字,寫滿就要翻頁。再給你n個數字,第i天要寫ai個名字。問你每天各要翻幾頁?

模擬題,沒啥好說的。

B.Segment Occurrences

題意,給你兩個字串s和t。問在s的[l,r]中t出現的次數是多少。又是模擬題,為了方便起見,我們考慮從s中的i位置開始接下來的連續的一串字母是不是和t相同,如果是,說明這個位置開始是可以出現t的。我們對它做一下字首和,就可以輕鬆應付大量的詢問次數。

對於詢問[l,r],我們將r處理為r’=r-t.length()+1。然後如果此時r’已經到l的左邊,那麼無解,否則ans=sum[r’]-sum[l-1]。

#include<iostream>
#include<cstring>
using namespace std;

#define MAX(x,y) (((x)>(y))?(x):(y))
string s,t;
int lens,lent,q;
int num[6005];
int main()
{
    cin>>lens>>lent>>q;
    cin>>s>>t;
    memset(num,0,sizeof(num));
    for(int i=0;i<=lens-lent;i++)
    {
        int
flag=0; for(int j=0;j<lent;j++)if(s[i+j]!=t[j])flag=1; num[i+1]=num[i]; if(!flag)num[i+1]++; } for(int i=0;i<q;i++) { int l,r; cin>>l>>r; r=r-lent+1; if(r<l) { cout<<0<<endl; continue
; } cout<<num[r]-num[l-1]<<endl; } return 0; }

C.天下第一垃圾模擬題

看到小標題了嗎?好,我們看下一題。

D. Vasya And The Matrix

給你一個n*m的矩陣。然後分別給你每行和每列的xor,問你這個矩陣是什麼。如不存在輸出NO。

我看到第一反應是全部填0,然後右邊和下邊填對應的數,最後確定右下角的格子是否存在這樣的數。但是我沒辦法證明這個演算法是否正確,所以我沒這麼做。聽說是可以過的,希望各位大佬教我證明。

我這邊因為看到xor,所以想到拆位。我們考慮一個base=2^a,窮舉base直到超出範圍,分別統計每一位每個矩陣的格子裡為1還是0,最後加起來得到最終結果。

這樣說有點抽象,我們舉個例子。

比方說題目是這樣(x是要求的數,邊上的數字是異或和)
□ 5 3 13
2 x x x
9 x x x

轉換成二進位制

□□□□ 0101 0011 1101
0010 xxxx xxxx xxxx
1001 xxxx xxxx xxxx

我們先拆base=2^0=1

□ 1 1 1
0 x x x
1 x x x

顯然,對於異或和為1的某行(或某列),其中的1的個數應該是奇數,反之為偶數個,所以橫著的1的個數和豎著的1的個數的奇偶應該相同。否則如果橫著是奇數,豎著是偶數,那麼我們用上面的推論,可以分別得出拆位矩陣中1的個數既是奇數個,又是偶數個,矛盾,vice verse。

當奇偶相同以後,我們用以下的策略填:

1.先找行1和列1的交匯格填1,這樣能解決同樣多個行1和列1

□ 1 1 1
0 x x x
1 1 x x

2.由於上面的推論,一定剩下偶數個行1(或列1),那麼把它們填在同列(同行),因為偶數個所以不會影響所填的列(行)的狀態。

□ 1 1 1
0 x 1 1
1 1 x x

3.其餘位置填0

□ 1 1 1
0 0 1 1
1 1 0 0

最後把表更新進答案就完事了

#include<iostream>
#include<cstring>
using namespace std;

#define LL long long
LL ans[105][105];
int row[105],col[105];
LL a[105],b[105];
LL n,m;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=m;i++)cin>>b[i];
    memset(ans,0,sizeof(ans));
    long long base=1;
    while(base<1e9+7)
    {
        memset(row,0,sizeof(row));
        memset(col,0,sizeof(col));
        int cntr=0,cntc=0;
        for(int i=1;i<=n;i++)if(a[i]&base)row[i]=1,cntr++;
        for(int i=1;i<=m;i++)if(b[i]&base)col[i]=1,cntc++;
        if((cntr&1)^(cntc&1))break;

        if(cntr>cntc)
        {
            int cnt=cntc;
            int pntr=0,pntc=0;
            while(cnt--)
            {
                pntr++;
                pntc++;
                while(!row[pntr])pntr++;
                while(!col[pntc])pntc++;
                ans[pntr][pntc]+=base;
            }
            cnt=cntr-cntc;
            while(cnt--)
            {
                if(cntc==0)pntc=1;
                pntr++;
                while(!row[pntr])pntr++;
                ans[pntr][pntc]+=base;
            }
        }
        else
        {
            int cnt=cntr;
            int pntr=0,pntc=0;
            while(cnt--)
            {
                pntr++;
                pntc++;
                while(!row[pntr])pntr++;
                while(!col[pntc])pntc++;
                ans[pntr][pntc]+=base;
            }
            cnt=cntc-cntr;
            while(cnt--)
            {
                if(cntr==0)pntr=1;
                pntc++;
                while(!col[pntc])pntc++;
                ans[pntr][pntc]+=base;
            }
        }

        base=base*2;
    }
    if(base<1e9+7)cout<<"NO"<<endl;
    else
    {
        cout<<"YES"<<endl;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)cout<<ans[i][j]<<" ";
            cout<<endl;
        }
    }
}

補充對於填0方案的證明:

首先,我們不妨設兩邊分別為a1,a2…an;b1,b2…bm。

首先,如果這裡寫圖片描述,那麼答案一定是NO。
因為a1⊕a2⊕…⊕an和b1⊕b2⊕…⊕bm分別是矩陣中所有元素的異或和。如果他們不相等,那麼顯然是矛盾的。於是我們得到:

a1⊕a2⊕…⊕an⊕b1⊕b2⊕…⊕bm=0 (1)

接下來再看題目,對於題目:

□□ b1 b2 … bm
a1
a2

an

我們除了matrix[1][1]之外按照以下的方案填寫(因為排版,所有0用00表示):

□□ b1 b2 … bm
a1 xx b2 … bm
a2 a2 00 … 00
… … … … …
an an 00 … 00

最後只剩下matrix[1][1]沒有解決,我們希望構造出這一個解。由題意,不難得出:

matrix[1][1]⊕a2⊕…⊕an=b1 (2)

matrix[1][1]⊕b2⊕…⊕bm=a2 (3)

移項後得到:

matrix[1][1]=a2⊕…⊕an⊕b1 (4)

matrix[1][1]=b2⊕…⊕bm⊕a2 (5)

好的,那麼接下來這邊只需要證明(4)(5)的右邊相等即可。此時我們對
a1⊕a2⊕…⊕an⊕b1⊕b2⊕…⊕bm=0(1)進行變形,得到:

a2⊕…⊕an⊕b1 = b2⊕…⊕bm⊕a2 = matrix[1][1]

那麼說明這樣的matrix[1][1]是存在且唯一的,於是證明完畢。

E.Rest in the Shades

看似很複雜的一道題,其實還是模擬。。

題意是給你一個勻速移動的光源,再給你一些屏障(在同一條線上),詢問一些點無法被光源照射的時長。這道題因為有兩組大型資料(屏障,點),所以必定是對其中一個進行預處理。那麼我們不難想到,不妨將詢問點作為光源,反向處理,其實本質是一樣的。

為了提高執行效率,我們再處理一下字首和,用二分加速一下(好像不二分的話會TLE),花上大量的時間去模擬。叮!AC!

特別提醒一下。我打二分的時候一直忘記要把上下界定在答案外面,這邊我調了好久才調對,所以略作提醒。此外,用&判斷奇偶性的時候,記住&的優先順序是低於==的,所以判斷為偶數的時候要加括號。

#include<iostream>
#include<cmath>
using namespace std;

const double EPS=1e-6;
double s,a,b,t,l,r;
long long n,q,tmp;
double sum[400005],fence[400005],x,y;

void read(long long &x)
{
    x=0;long long f=-1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return;
}

bool equ(double a,double b)
{
    if(fabs(a-b)<EPS)return 1;
    return 0;
}

int findfi(double x)
{
    int ll=0,rr=2*n+1;
    while(ll<rr-1)
    {
        int mid=(ll+rr)>>1;
        if(fence[mid]>x)rr=mid;
        else ll=mid;
    }
    return rr;
}

int finden(double x)
{
    int ll=0,rr=2*n+1;
    while(ll<rr-1)
    {
        int mid=(ll+rr)>>1;
        if(fence[mid]>x)rr=mid;
        else ll=mid;
    }
    return ll;
}

int main()
{
    cin>>s>>a>>b;
    t=b-a;

    read(n);
    sum[0]=0;
    for(int i=1;i<=2*n;i++)
    {
        read(tmp);
        fence[i]=tmp;
        sum[i]=sum[i-1];
        if(i%2==0)sum[i]+=fence[i]-fence[i-1];
     } 
     fence[0]=0;fence[2*n+1]=1e10+9;

     read(q);
     for(int i=0;i<q;i++)
     {
        read(tmp);x=tmp;
        read(tmp);y=tmp;
        if(equ(x,a))
        {
            l=x;
            r=(b*y-x*s)/(y-s);
         }
         else if(equ(x,b))
         {
            r=x;
            l=(a*y-x*s)/(y-s);
         }
         else
         {
            l=(a*y-x*s)/(y-s);
            r=(b*y-x*s)/(y-s);
         }
         double zeroo=0;
         if(l>fence[2*n]||r<fence[1])printf("%.10f\n",zeroo);
         else
         {
             int fi=findfi(l);
             int en=finden(r);
             double whole=r-l,partt=sum[en]-sum[fi];
             if(en<fi)
             {
                if(en&1)partt=whole;
                else partt=0;
             }
             else
             {
                 if(fi%2==0)
                 {
                    double pre=fence[fi];
                    partt+=pre-l;
                 }
                 if(en&1)
                 {
                    double post=fence[en];
                    partt+=r-post;
                 }
             } 
             double rat=(partt/whole)*t;
             printf("%.10f\n",rat);
         }
     }

}