Codeforces #275 Div 1 簡要題解

比賽總結

這次比賽打得總算像樣了。。。
做了A和B,都是wa了兩次才ac,罰時有點慘
在正式和非正式選手裏排名537名,在正式選手裏排名450名

A. Diverse Permutation

題目鏈接

http://codeforces.com/contest/482/problem/A

題目大意

要你構造一個1,2,...n 的排列a[] ,使得最終|a[i]a[i+1]| 的互不相同的值恰好有k

思路

顯然,|a[i]a[i+1]| 的互不相同的值最多時,構造的排列一定是
1 n 2 n1 3 n2

因此,我們可以先構造排列的前K 個數字,這部分數字滿足 1 n 2 n1 3 n2 …的特點,這樣就出現了K1 種。然後剩下的部分,我們就單調遞增或遞減填入沒填過的數字,這樣就出現了第K 種取值,也就是1

K=1 時特判下就好了

代碼

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXN 1100000

using namespace std;

int n,K;
int tot=0;

int main()
{
    scanf("%d%d",&n,&K);
    if(K==1)
    {
        for(int i=1;i<=n;i++) printf("%d ",i);
        printf("\n");
        return 0;
    }
    K--;
    int a=1,b=n+1;
    printf("1 ");
    int i=2;
    for(int t=1;t<=(K-1);t++,i++)
        printf("%d ",(i&1)?++a:--b);
    bool flag=(i&1)?true:false;
    for(;i<=n;i++)
        if(flag) printf("%d ",++a);
        else printf("%d ",--b);
    printf("\n");
    return 0;
}

B. Interesting Array

題目鏈接

http://codeforces.com/contest/482/problem/B

題目大意

要你構造一個長度爲n 的數列,滿足m 個要求。對於每個要求,要使得[Li,Ri] 區間內的數列數字的與和爲val

思路

我們首先討論val1 的情況
注意到,一個區間內的數列數字的與和爲1 ,就必須滿足這個區間裏所有數字都是1,而一個區間內的數列數字的與和爲0 ,只需要滿足這個區間裏有一個數字是0即可。假設一個與和爲1的區間[Li,Ri] 和一個與和爲0的區間[Li,Ri] 有交集,那麼相交的那部分肯定是取1。

這個特點非常類似於或運算:重疊的那部分先是區間或了1,然後是區間或了0,最終得到了連續的一段1,於是問題轉變爲:給你一個初始全爲0的序列,每次區間或某個數字,然後離線對每個詢問檢查一遍,每個詢問對應的區間[Li,Ri] 的與和是否爲val 。並且要輸出最終的序列

這個問題非常簡單,直接維護一個支持區間修改、區間查詢的線段樹即可。維護區間或的lazy tag、以及區間與和標記。最終遍歷一遍線段樹的所有葉子節點即可

代碼

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXN 110000

using namespace std;

int or_tag[MAXN<<2],andsum_tag[MAXN<<2]; //區間或標記,區間與和標記

void pushup(int o)
{
    andsum_tag[o]=andsum_tag[o<<1]&andsum_tag[o<<1|1];
}

void pushdown(int o)
{
    or_tag[o<<1]|=or_tag[o];
    andsum_tag[o<<1]|=or_tag[o];
    or_tag[o<<1|1]|=or_tag[o];
    andsum_tag[o<<1|1]|=or_tag[o];
    or_tag[o]=0;
}

void update(int o,int L,int R,int ql,int qr,int orval)
{
    if(ql<=L&&R<=qr)
    {
        andsum_tag[o]|=orval;
        or_tag[o]|=orval;
        return;
    }
    pushdown(o);
    int M=(L+R)>>1;
    if(ql<=M) update(o<<1,L,M,ql,qr,orval);
    if(qr>M) update(o<<1|1,M+1,R,ql,qr,orval);
    pushup(o);
}

int query(int o,int L,int R,int ql,int qr)
{
    if(ql<=L&&R<=qr)
    {
        return andsum_tag[o];
    }
    pushdown(o);
    int M=(L+R)>>1,ans=1; //mlgb!!!不能拿ans=1然後and答案,這樣的話可能會錯!
    if(qr<=M) return query(o<<1,L,M,ql,qr);
    if(ql>M) return query(o<<1|1,M+1,R,ql,qr);
    //cout<<"L: "<<L<<" R: "<<R<<" ans: "<<ans<<endl;
    return query(o<<1,L,M,ql,qr)&query(o<<1|1,M+1,R,ql,qr);
}

void print(int o,int L,int R)
{
    if(L==R)
    {
        printf("%d ",andsum_tag[o]);
        return;
    }
    pushdown(o);
    int M=(L+R)>>1;
    print(o<<1,L,M);
    print(o<<1|1,M+1,R);
}

struct Query
{
    int L,R,val;
}querys[MAXN];

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&querys[i].L,&querys[i].R,&querys[i].val);
        update(1,1,n,querys[i].L,querys[i].R,querys[i].val);
    }
    for(int i=1;i<=m;i++)
    {
        if(query(1,1,n,querys[i].L,querys[i].R)!=querys[i].val)
        {
            printf("NO\n");
            return 0;
        }
    }
    printf("YES\n");
    print(1,1,n);
    return 0;
}

C. Interesting Array

題目鏈接

http://codeforces.com/contest/482/problem/C

題目大意

n個長爲m的字符串,A會先從中隨機選擇一個串,讓B去判斷A選擇的串是哪個串,每次B會隨機地知道m位字母裏未知的一位,問B確定答案的期望步數

思路

我們可以先預處理出f[S]=m 個下標中已經知道了集合S 裏的下標的概率,以及cnt[S]=m 個下標中已經知道了集合S 裏的下標,會找出多少個相同的字符串(只能找出一個字符串,就是0)。
答案就是f[S]cnt[S]n

爲什麼呢?假設A不是隨機選取一個串,而是指定了一個串。假如當前B已知的字符的下標集合爲S ,若cnt[S]=0 ,表明B同學不需要再詢問了,直接結束,否則cnt[S]>=2 ,會找出至少2個暫時看起來一樣的字符串,B同學至少還需要再走一步,這樣的話步數會+1,對期望步數的答案的貢獻爲f[S]1

而如果A是指定了一個串呢?最終的期望步數就是ni=1in ,我們可以直接利用上一段的做法來求出ni=1icnt[S]>=2 時,會找出至少2個暫時看起來一樣的字符串,B同學至少還需要再走一步,這樣的話,對於這cnt[S] 個字符串,要將它們一一區分開來,每個字符串的判斷步數都會+1,因此對期望步數之和的答案的貢獻爲f[S]cnt[S]

代碼

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXN 60

using namespace std;

typedef long long int LL;

int n,m;
char s[MAXN][MAXN];
LL cnt[1<<22]; //cnt[S]=集合S裏每個下標對應字母均相同的字符串集合
double f[1<<22]; //f[S]=已經知道了集合S裏的下標對應位置的字母的概率

int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%s",s[i]);
    m=strlen(s[0]);
    for(int i=0;i<n;i++) //這裏重複枚舉也沒事,|運算重複一次的話結果不會改變
        for(int j=0;j<n;j++)
            if(i!=j)
            {
                int tmp=0;
                for(int k=0;k<m;k++)
                    if(s[i][k]==s[j][k])
                        tmp|=(1<<k);
                cnt[tmp]|=((LL)1<<(LL)j);
            }
    for(int S=(1<<m)-1;S>=0;S--) //cnt[S],cnt[S'],若S'是S子集,則cnt[S']|=cnt[S]
        for(int i=0;i<m;i++)
            if(S&(1<<i))
                cnt[S^(1<<i)]|=cnt[S];
    double ans=0;
    f[0]=1;
    for(int S=0;S<(1<<m);S++)
    {
        int tot=0; //tot=集合S裏已經確定的下標個數
        for(int i=0;i<m;i++)
            if(S&(1<<i))
                tot++;
        for(int i=0;i<m;i++)
            if(!(S&(1<<i)))
            {
                f[S|(1<<i)]+=f[S]/(m-tot);
            }
        for(int i=0;i<n;i++)
            if(cnt[S]&((LL)1<<(LL)i))
                ans+=f[S];
    }
    printf("%.10lf\n",ans/(double)n);
    return 0;
}
發佈了378 篇原創文章 · 獲贊 10 · 訪問量 32萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章