【Google Kickstart Round_H】解题报告

版权声明:本文为博主原创文章,禁止所有形式的未经博主允许的转载行为。 https://blog.csdn.net/qq_33330876/article/details/84199957

虽然做了一个多小时,但是还是想说这场好水啊。
链接:https://code.google.com/codejam/contest/3324486/dashboard


Problem A. Big Buttons

题意

一个长度为 n n 的 01串,该字符串有 m m 个不能出现的前缀,问这样的字符串有多少种?

题解

由于 n , m n,m 的范围很小,直接建一棵表示前缀限制条件的字典树,然后在上面通过限制条件从可能方案中剪去即可。

代码

#include <bits/stdc++.h>
using namespace std;

const int maxn=int(1e5)+11;
int n,m,tot;
int to[maxn][2], mark[maxn];
char s[maxn];
long long ans=0;

int get_id(char ch) {return ch=='B';}
void ins(char *s,int len) {
	int cur=0;
	for(int i=1;i<=len;++i) {
		int ch=get_id(s[i]);
		if(!to[cur][ch]) to[cur][ch]=++tot;
		cur=to[cur][ch];
	}
	mark[cur]++;
	return;
}

void init() {
	tot=0;
	for(int i=0;i<maxn;++i) {
		to[i][0]=to[i][1]=0;
		mark[i]=0;
	}
	return;
}

void dfs(int k,int dep) {
	if(mark[k]) {
		ans-=(1ll<<dep);
		return;
	}
	if(to[k][0]) dfs(to[k][0],dep-1);
	if(to[k][1]) dfs(to[k][1],dep-1);
	return;
}

int cas=0;
void work() {
	init();
	scanf("%d%d",&n,&m);
	register int i;
	for(i=1;i<=m;++i) {
		scanf("%s",s+1);
		ins(s,strlen(s+1));
	}
	ans=(1ll<<n);
	dfs(0,n);
	printf("Case #%d: %lld\n",++cas,ans);

	return;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("input1.txt","r",stdin);
	freopen("output1.txt","w",stdout);
#endif
	int T;
	for(scanf("%d",&T);T;T--)
		work();

	return 0;
}

Problem B. Mural

题意

有一个由 0~9 的数字组成的数列,第一次可以随便取一个数字,接下来的每一次只能取一个与已取区间相邻的数字。且每次取出一个数字后,数列两端的两个数之一会随机地变为不可取。问最坏情况下取出数字的最大和是多少。

题解

首先知道我们一定能取出一个长度为 ( n + 1 ) / 2 (n+1)/2 的连续区间;
而且,不管不可取的数字是如何出现在两端的,对于一个长度为 ( n + 1 ) / 2 (n+1)/2 的区间,一定存在一种方法把它取到。
所以 O ( n ) O(n) 找出区间和最大的长度为 ( n + 1 ) / 2 (n+1)/2 的连续区间即可。

代码

#include <bits/stdc++.h>
using namespace std;

const int maxn=int(5e6)+11;
int n;
char s[maxn];
int sum[maxn];

int cas=0;
void work() {
	scanf("%d%s",&n,s+1);
	register int i;
	for(i=1;i<=n;++i)
		sum[i]=sum[i-1]+s[i]-'0';

	int ans=0, len=(n+1)/2;
	for(i=1;(i+len-1)<=n;++i)
		ans=max(ans,sum[i+len-1]-sum[i-1]);
	printf("Case #%d: %d\n",++cas,ans);

	return;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("input2.txt","r",stdin);
	freopen("output2.txt","w",stdout);
#endif
	int T;
	for(scanf("%d",&T);T;T--)
		work();

	return 0;
}

Problem C. Let Me Count The Ways

题意

n n 对情侣坐一起,其中的 m m 对情侣中的两人不能坐相邻位置,考虑情侣中的二人是不同的。问有多少种安排座位的方法?

题解

处理出 g ( i ) g(i) ,表示在那 m m 对不能坐一起的情侣中,至少有 i i 对情侣坐在一起的安排方案数。然后容斥一下出答案。
g ( i ) = C m i ( 2 n i ) ! 2 i a n s = i = 0 m ( 1 ) i g ( i ) g(i)=C_m^i*(2n-i)!*2^i \\ ans=\sum_{i=0}^{m} (-1)^i*g(i)

代码

#include <bits/stdc++.h>
using namespace std;

const int moder=int(1e9)+7;
inline int add(int a,int b) {return (a+b<moder)?a+b:a+b-moder;}
inline int mul(int a,int b) {return 1ll*a*b%moder;}
inline int les(int a,int b) {return a>=b?a-b:(a+moder-b);}

const int maxn=int(3e5)+11;
int n,m;
int fact[maxn], inv[maxn];

int C(int m,int n) {
	return mul(fact[n],mul(inv[m],inv[n-m]));
}
int fpow(int a,int k) {
	int res=1;
	for(;k;k>>=1,a=mul(a,a)) if(k&1) res=mul(res,a);
	return res;
}
void init() {
	fact[0]=inv[0]=1;
	for(int i=1;i<maxn;++i) fact[i]=mul(fact[i-1],i), inv[i]=fpow(fact[i],moder-2);
	return;
}

int cas=0;
int g[maxn];
void work() {
	scanf("%d%d",&n,&m);
	for(int i=0;i<=m;++i)
		g[i]=mul(C(i,m),mul(fact[2*n-i],fpow(2,i)));
	int ans=0;
	for(int i=0;i<=m;++i) {
		if(i&1) ans=les(ans,g[i]);
		else ans=add(ans,g[i]);
	}
	printf("Case #%d: %d\n",++cas,ans);
	return;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("input1.txt","r",stdin);
	freopen("output1.txt","w",stdout);
#endif
	init();
	int T;
	for(scanf("%d",&T);T;T--)
		work();

	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_33330876/article/details/84199957