1. 程式人生 > >Codeforces Round #502 ( Div. 1 + Div. 2)

Codeforces Round #502 ( Div. 1 + Div. 2)

A.The Rank

手速題,給你一堆人的成績,問你其中第一個人的總分排第幾(總分相同按出現順序排)。

那儲存第一個人的成績,先放在第一名,然後後面每有一個成績比他高的,排名就加一。最後複雜度是O(n)。

我手賤,打了個結構體,大家就當沒看見。

struct stu
{
    int id,a,b,c,d;
 } s[1005];

 bool operator > (stu x,stu y)
 {
    if(x.a+x.b+x.c+x.d>y.a+y.b+y.c+y.d)return 1;
    return 0;
 }

 int main()
 {
    int
n,ans; cin>>n; ans=1; stu smith; cin>>smith.a>>smith.b>>smith.c>>smith.d; for(int i=1;i<n;i++) { stu tmp; cin>>tmp.a>>tmp.b>>tmp.c>>tmp.d; if(tmp>smith)ans++; } cout<<ans; return
0; }

B.The Bits

給你一個長度為n的01串a,再給你一個等長的01串b。然後我們有一個c=a&b。現在我們可以把a中任意兩個字元交換位置,得到a’,有c’=a’&b。問有多少種換法,使得c’!=c。

因為資料範圍是1e5,如果窮舉的話,也就1e10,還是有機率可以過的嘛。但是我們很快發現這道題有很強的相似性,對於a,b在同一個位置上的數,總共只有四種狀態,即:

○ A B
① 0 0
② 0 1
③ 1 0
④ 1 1

因為我們只能互換a中的數字,那麼總共有C(4,2)種可能的換法。窮舉之後發現①③,①④,②③中的A交換以後會改變,而其他換法不會改變。所以我們只要統計四種狀態分別的個數,最後結果就是① * ③ + ① * ④ + ② * ③。

int main()
{
    int n;
    string a,b;
    long long cnt1=0,cnt2=0,cnt3=0,cnt4=0;
    cin>>n>>a>>b;
    for(int i=0;i<n;i++)
    {
        if(a[i]=='0'&&b[i]=='0')cnt1++;
        if(a[i]=='0'&&b[i]=='1')cnt2++;
        if(a[i]=='1'&&b[i]=='0')cnt3++;
        if(a[i]=='1'&&b[i]=='1')cnt4++;
    }
    cout<<(cnt1*cnt3+cnt1*cnt4+cnt2*cnt3)<<endl;
    return 0;
 } 

C.The Phone number

讓你求一個N的全排列,使它的最長上升子序列的長度與最長下降子序列的長度之和最小。

我的直覺告訴我,分塊。。。

(其實我一開始是中間切開做的,後來被大佬提醒才對了,太真實了)

那反正就是取q=sqrt(n),把n切成儘量等長的q個小段,然後。。比較難解釋,舉個例子,比如n=13,那麼q=3。我們把它切成長度為4,4,5的三段,然後:

4 3 2 1 / 8 7 6 5 /13 12 11 10 9

這樣上升子序列的長度是3(大段個數),下降子序列長度是5(小段長度的最大值),結果就是8。證明的話,我沒想到特別嚴謹的證明,就希望有大佬提供思路。本蒻簡陋證明如下:

首先,根據上面的構造法,我們得到的答案大小為q+(n+q-1)/q,記為M,我們假設存在一個比它更小的答案m。

我們考慮一個長度為n-1的以及排好的全排列,然後向裡面插入一個n。可以證明,如果放在第一個,那麼所有原來的下降序列長度+1;如果在最後一個,那麼所有原來的上升序列長度+1;如果再中間,那麼就使左邊的上升序列長度+1,右邊的下降序列長度+1。

n==1時,答案為2(1+1)。

對於每一次操作,若放在最左或最右,必定使答案+1。那麼最終答案是n+1。

而若是放在中間,我們把兩邊的撇開,裡面是一個類似的問題。然後通過某種玄學的方法,我們可以認為最終結果應該是和sqrt(n)有關的一個數,也就是分塊的結果。

int main()
{
    int n;
    cin>>n;
    int p=sqrt(n),q=n/p;
    int cnt=q;
    for(int i=0;i<p;i++)
    {
        for(int j=0;j<q;j++)
        {
            cout<<cnt<<" ";
            cnt--;
        }
        cnt+=2*q;
    }
    if(p*q<n)for(int i=n;i>p*q;i--)cout<<i<<" ";
    return 0;
}

D.The Wu

這是一道比較複雜的模擬題,難度不大,關鍵在於位運算和預處理。
http://codeforces.com/contest/1017/problem/D

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

int bitread()
{
    int base=1;
    int x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){ch=getchar();}
    while(ch>='0'&&ch<='9')
    {
        if(ch=='1')x+=base;
        ch=getchar();
        base=base*2;
    }
    return x;
}

int lowbit(int x)
{
    return x&-x;
}

int w[4100];
int cnt[4100];
int ans[4100][105]; 
int n,m,q;
int N;

int lazy_tag[105];

int main()
{
    memset(cnt,0,sizeof(cnt));
    memset(ans,0,sizeof(ans));
    read(n);read(m);read(q);
    N=1<<n;
    for(int i=0;i<n;i++)read(w[1<<i]);
    for(int i=0;i<((N));i++)
    {
        int sum=0;
        int t=i;
        while(t)
        {
            int lb=lowbit(t);
            sum+=w[lb];
            t-=lb;
        }
        w[i]=sum;
    }
    for(int i=0;i<m;i++)cnt[bitread()]++;

    for(int i=0;i<((N));i++)
    {
        memset(lazy_tag,0,sizeof(lazy_tag));
        for(int j=0;j<((N));j++)
        {
            int b=i^j^((N)-1);
            if(w[b]>100)continue;
            lazy_tag[w[b]]+=cnt[j];
    //      for(int k=w[b];k<=100;k++)ans[i][k]+=cnt[j];
        }
        int summ=0;
        for(int hh=0;hh<=100;hh++)
        {
            summ+=lazy_tag[hh];
            ans[i][hh]+=summ;
        }
    }

    while(q--)
    {
        int aa,bb;
        aa=bitread();
        read(bb);
        cout<<ans[aa][bb]<<endl;    
    }
}