联赛模拟测试 16

A.阴阳

dp,不会。

B.简单的序列

两种解法。

(large Solution1)

设左括号为 (1) ,右括号为 (-1) ,那么最终合法序列为任意前缀和都不小于 (0) 的方案。
所以先 (n^2) dp一下,处理出来 (i) 个数 ,和为 (j) 的方案数。
处理一下给出的中间序列的前缀和,和中间序列最小的值,下边枚举左边序列长度和和。
枚举序列和为 (j) ,需要满足 (0leqslant j+sum)(0leqslant j+min) 。枚举即可。

(large Solution2)
卡特兰数,可以由中间序列先确定左边序列有多少个左括号,右边序列同理,然后可以枚举左边长度,和左括号的个数。
根据卡特兰数求解。

代码



#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
const int maxn = 2e5 + 10;
#define ll long long
ll jc[maxn],jcny[maxn],ny[maxn];
char s[maxn];
inline ll getC(int n,int m){return jc[n] % mod * jcny[n-m] % mod * jcny[m] % mod;}
inline ll calc(int n,int m){
	if((n + m) & 1)return 0;
	return (getC(n,(n+m)/2) - getC(n,(n+m)/2+1) + mod ) % mod;
}
int main(){
	freopen("bracket.in","r",stdin);
	freopen("bracket.out","w",stdout);
	jc[0] = jcny[0] = ny[1] = 1;
	int n,m;
	for(int i = 2;i <= 1e5;++i)ny[i] = ny[mod % i] % mod * (mod - mod / i) % mod;
	for(int i = 1;i <= 1e5;++i)jc[i] = jc[i-1] * i % mod,jcny[i] = jcny[i-1] * ny[i] % mod;
	scanf("%d%d",&n,&m);scanf("%s",s+1);
	int cntl = 0,cntr = 0;
	for(int i = 1;i <= m;++i){
		if(s[i] == '(')cntr++;
		else{
			if(cntr)cntr--;
			else cntl++;
		}
	}
	int sum = n - m;
	ll ans = 0;
	for(int i = cntl;i <= sum;++i){
		for(int j = cntl;j <= i;++j){
			ans = (ans + calc(i,j) * calc(sum-i,j-cntl+cntr) % mod) % mod;
		}
	}
	printf("%lld
",ans);
	return 0;
}

C.简单的期望

dp神题,由于最多 (200) 次加一,所以可以只记录后八位,然后看第九位是谁,并且有多少个连续的即可。
定义 (f[i][S][k][0/1]) 表示前 (i) 次操作,后八位为 (S) ,第九位后有 (k) 个连续的,第九为是 (0)(1)
然后八种转移即可。

代码



#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e2+30;
const int mx = (1 << 8) - 1;
#define db double
db f[202][(1<<8)+10][235][4];
inline int calc(int s){
	int cnt = 0;
	while(s % 2 == 0){
		s /= 2;
		cnt++;
	}
	return cnt;
}
int main(){
	freopen("exp.in","r",stdin);
	freopen("exp.out","w",stdout);
	long long x,n;db p;
	scanf("%lld%lld%lf",&x,&n,&p);
	p = p / 100;
	db q = 1.0 - p;
	int cnt;
	int tmp = x & (1 << 8);
	if((x >> 8) == 0)cnt = 1;
	else{
		cnt = 0;
		for(int i = 9;i <= 30;++i){
			int jl = x & (1 << i);
			cnt++;
			if(jl != tmp)break;
		}
	}
	f[0][x & mx][cnt][tmp] = 1.0;
	int base = 230;
	for(int i = 1;i <= n;++i){
		for(int j = 0;j < mx;++j){
			for(int k = 1;k <= base;++k){
				f[i][j+1][k][0] += f[i-1][j][k][0] * q;
				f[i][j+1][k][1] += f[i-1][j][k][1] * q;
			}
		}
		for(int j = 1;j <= base;++j){
			f[i][0][j][0] += f[i-1][mx][j][1] * q;
		}
		for(int j = 1;j <= base;++j){
			f[i][0][1][1] += f[i-1][mx][j][0] * q;
		}
		for(int j = 0;j < 128;++j){
			for(int k = 1;k <= base;++k){
				f[i][j<<1][1][0] += f[i-1][j][k][1] * p;
				f[i][j<<1][k+1][0] += f[i-1][j][k][0] * p;
			}
		}
		for(int j = 128;j <= mx;++j){
			for(int k = 1;k <= base;++k){
				f[i][(j << 1) & mx][k+1][1] += f[i-1][j][k][1] * p;
				f[i][(j << 1) & mx][1][1] += f[i-1][j][k][0] * p;
			}
		}
	}
	db ans = 0;
	for(int i = 1;i <= mx;++i){
		for(int j = 1;j <= base;++j){
			ans += (f[n][i][j][0] + f[n][i][j][1]) * calc(i);
		}
	}
	for(int i = 1;i <= base;++i){
		ans += f[n][0][i][0] * (i + 8) + f[n][0][i][1] * 8;
	}
	printf("%.8lf
",ans);
	return 0;
}

D. 简单的操作

容易想到奇环一定会转化成三元环导致无解,所以先染色判奇环。
由于图可能不联通,所以根据手模可以得到,每个联通块内的最长路径就是链的最长,最短路求和即可。

(Never Give Up)

原文地址:https://www.cnblogs.com/Vocanda/p/13832080.html