比赛总结
这次比赛打得总算像样了。。。
做了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;
}