比賽總結
這次比賽打得總算像樣了。。。
做了A和B,都是wa了兩次才ac,罰時有點慘
在正式和非正式選手裏排名537名,在正式選手裏排名450名
A. Diverse Permutation
題目鏈接
http://codeforces.com/contest/482/problem/A
題目大意
要你構造一個
思路
顯然,
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
題目大意
要你構造一個長度爲
思路
我們首先討論
注意到,一個區間內的數列數字的與和爲
這個特點非常類似於或運算:重疊的那部分先是區間或了1,然後是區間或了0,最終得到了連續的一段1,於是問題轉變爲:給你一個初始全爲0的序列,每次區間或某個數字,然後離線對每個詢問檢查一遍,每個詢問對應的區間
這個問題非常簡單,直接維護一個支持區間修改、區間查詢的線段樹即可。維護區間或的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確定答案的期望步數
思路
我們可以先預處理出
答案就是
爲什麼呢?假設A不是隨機選取一個串,而是指定了一個串。假如當前B已知的字符的下標集合爲
而如果A是指定了一個串呢?最終的期望步數就是
代碼
#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;
}