zoukankan      html  css  js  c++  java
  • AtCoder Regular Contest 104 题解

    A

    题意简述

    给定 (A)(B),求 (X)(Y) 使得 (X+Y=A,X-Y=B)

    Solution

    答案为 (frac{A+B}2)(frac{A-B}2)

    Code

    #include <bits/stdc++.h>
    
    int a, b;
    
    int main()
    {
    	std::cin >> a >> b;
    	std::cout << (a + b) / 2 << " " << (a - b) / 2 << std::endl;
    	return 0;
    }
    

    B

    题意简述

    给定一个 DNA 序列,串长不超过 (5000),求有多少个子区间,满足其存在一个排列与原子区间互补配对(A-TC-G)。

    Solution

    题目条件即为子区间内 AT 个数相等,CG 相等,直接枚举子区间,前缀和判断即可,(O(n^2))

    Code

    #include <bits/stdc++.h>
    
    const int N = 5005;
    
    int n, sA[N], sC[N], sG[N], sT[N], ans;
    char s[N];
    
    int main()
    {
    	scanf("%d%s", &n, s + 1);
    	for (int i = 1; i <= n; i++)
    	{
    		sA[i] = sA[i - 1] + (s[i] == 'A');
    		sC[i] = sC[i - 1] + (s[i] == 'C');
    		sG[i] = sG[i - 1] + (s[i] == 'G');
    		sT[i] = sT[i - 1] + (s[i] == 'T');
    	}
    	for (int l = 1; l <= n; l++)
    		for (int r = l; r <= n; r++)
    			if (sA[r] - sA[l - 1] == sT[r] - sT[l - 1]
    				&& sC[r] - sC[l - 1] == sG[r] - sG[l - 1])
    					ans++;
    	return std::cout << ans << std::endl, 0;
    }
    

    C

    题意简述

    (N) 个区间,端点为 (1sim2N) 中的数值,每个端点被使用恰好一次。

    额外条件:如果两个区间相交,则要求这两个区间等长。

    给出这些区间的部分端点,求是否存在一种方案还原剩下的所有端点位置,使得上面的条件被满足。

    (Nle 100)

    Solution

    结论:满足条件时,(1sim2N)(2N) 个数可以被划分成若干个长度为偶数的段,使得没有任何区间的两端点分别在两个不同的段内,并且对于一个长度为 (2x) 的段 ([L,R]),这个段内的区间符合 ([L,L+x])([L+1,L+x+1])([L+2,L+x+2])(dots)([R-x,R]) 的形式。

    直接分段 DP 即可,(O(N^3))

    不过这题有一个很大的坑点(一遍 AC 这题的人是变态):对于两个区间 ([L,*])([*,R]),我们不能强行让 (L)(R) 成为一个区间的两端点。

    这个细节坑了不少人(也包括我)几十分钟。

    Code

    #include <bits/stdc++.h>
    
    template <class T>
    inline void read(T &res)
    {
    	res = 0; bool bo = 0; char c;
    	while (((c = getchar()) < '0' || c > '9') && c != '-');
    	if (c == '-') bo = 1; else res = c - 48;
    	while ((c = getchar()) >= '0' && c <= '9')
    		res = (res << 3) + (res << 1) + (c - 48);
    	if (bo) res = ~res + 1;
    }
    
    const int N = 105, M = 205;
    
    int n, a[N], b[N], c[M], o[M];
    bool ava[M][M], f[M];
    
    bool in(int l, int r, int x) {return l <= x && x <= r;}
    
    int main()
    {
    	read(n);
    	for (int i = 1; i <= n; i++) read(a[i]), read(b[i]);
    	memset(c, -1, sizeof(c));
    	for (int i = 1; i <= n; i++)
    	{
    		if (a[i] != -1)
    		{
    			if (c[a[i]] != -1) return puts("No"), 0;
    			c[a[i]] = 0; o[a[i]] = i;
    		}
    		if (b[i] != -1)
    		{
    			if (c[b[i]] != -1) return puts("No"), 0;
    			c[b[i]] = 1; o[b[i]] = i;
    		}
    		if (a[i] != -1 && b[i] != -1 && a[i] >= b[i])
    			return puts("No"), 0;
    	}
    	for (int l = 1; l <= (n << 1); l += 2)
    		for (int r = l + 1; r <= (n << 1); r += 2)
    		{
    			int d = (r - l + 1) >> 1; ava[l][r] = 1;
    			for (int i = 1; i <= n; i++)
    				if (a[i] != -1 && b[i] != -1)
    				{
    					if (in(l, r, a[i]) ^ in(l, r, b[i])) ava[l][r] = 0;
    					if (in(l, r, a[i]) && in(l, r, b[i])
    						&& b[i] - a[i] != d) ava[l][r] = 0;
    				}
    			for (int i = 1; i <= d; i++)
    			{
    				if (c[l + i - 1] == 1 || c[l + d + i - 1] == 0)
    					ava[l][r] = 0;
    				if (c[l + i - 1] == 0 && c[l + d + i - 1] == 1
    					&& o[l + i - 1] != o[l + d + i - 1])
    						ava[l][r] = 0;
    			}
    		}
    	f[0] = 1;
    	for (int i = 2; i <= (n << 1); i += 2)
    		for (int j = 0; j < i; j += 2)
    			f[i] |= f[j] && ava[j + 1][i];
    	return puts(f[n << 1] ? "Yes" : "No"), 0;
    }
    

    D

    题意简述

    对于所有的 (1le xle N),求 (1sim N) 的所有整数各选 (0sim K) 个(不能一个都不选),选出的数平均数为 (x) 的方案数。

    (N,Kle 100)

    Solution

    把所有数都减去 (x),转化成和为 (0),不难发现这么处理之后可以分为三批:

    (1)([1,N-x]) 各选 (0sim K) 个,贡献为正;

    (2)([1,x-1]) 各选 (0sim K) 个,贡献为负;

    (3)选 (0sim K)(0)

    考虑处理前两部分,我们只需在前面先来一个 DP (f_{i,j}) 表示 (1sim i) 的数和为 (j) 的方案数,前两部分即为 (sum_if_{N-x,i}f_{x-1,i})

    设上式结果为 (s),则答案为 ((K+1)s-1)

    总时间复杂度 (O(N^3K))

    Code

    #include <bits/stdc++.h>
    
    const int N = 105, M = 6e5 + 5;
    
    int n, k, EI, f[N][M], MX;
    
    inline void add(int &a, const int &b) {if ((a += b) >= EI) a -= EI;}
    
    inline void sub(int &a, const int &b) {if ((a -= b) < 0) a += EI;}
    
    int main()
    {
    	std::cin >> n >> k >> EI;
    	f[0][0] = 1; MX = k * n * (n + 1) / 2;
    	for (int i = 1; i <= n; i++)
    	{
    		int mx = k * i * (i + 1) / 2, nx = (k + 1) * i;
    		for (int j = 0; j <= mx; j++)
    		{
    			f[i][j] = f[i - 1][j]; if (j >= i) add(f[i][j], f[i][j - i]);
    			if (j >= nx) sub(f[i][j], f[i - 1][j - nx]);
    		}
    	}
    	for (int i = 1; i <= n; i++)
    	{
    		int ans = 0;
    		for (int j = 0; j <= MX; j++)
    			ans = (1ll * f[n - i][j] * f[i - 1][j] + ans) % EI;
    		printf("%d
    ", (int) ((1ll * ans * (k + 1) - 1 + EI) % EI));
    	}
    	return 0;
    }
    

    E

    题意简述

    (N) 个数的序列,第 (i) 个整数在 ([1,A_i]) 间等概率随机,求该序列最长上升子序列长度的期望。

    (Nle 6)(A_ile 10^9)

    Solution

    考虑枚举这些数两两间的大小关系,具体枚举方法为先做一个贝尔数的搜索以确定相等关系,再全排列枚举大小顺序。

    确定了这个之后显然已经确定了 LIS 的长度,现在要求的就是满足这组大小关系的方案数。

    问题转化成给定 (m),求满足 (x_ile a_i) 且严格递增的序列 (x) 个数。

    这是一个经典问题,可以离散化值域分段之后组合数 DP。

    Code

    #include <bits/stdc++.h>
    
    template <class T>
    inline void read(T &res)
    {
    	res = 0; bool bo = 0; char c;
    	while (((c = getchar()) < '0' || c > '9') && c != '-');
    	if (c == '-') bo = 1; else res = c - 48;
    	while ((c = getchar()) >= '0' && c <= '9')
    		res = (res << 3) + (res << 1) + (c - 48);
    	if (bo) res = ~res + 1;
    }
    
    template <class T>
    inline T Min(const T &a, const T &b) {return a < b ? a : b;}
    
    const int N = 8, EI = 1000000007;
    
    int n, a[N], ans, bel[N], tot, p[N], r[N], mn[N], f[N], inv[N], cnt,
    sze[N], mx[N], C[N][N], g[N][N];
    
    void solve()
    {
    	for (int i = 1; i <= tot; i++) p[i] = i;
    	do
    	{
    		for (int i = 1; i <= tot; i++) mn[i] = EI;
    		for (int i = 1; i <= n; i++) r[i] = p[bel[i]],
    			mn[p[bel[i]]] = Min(mn[p[bel[i]]], a[i]);
    		int lis = 0;
    		for (int i = 1; i <= n; i++)
    		{
    			f[i] = 0;
    			for (int j = 1; j < i; j++)
    				if (r[j] < r[i] && f[j] > f[i]) f[i] = f[j];
    			f[i]++; if (f[i] > lis) lis = f[i];
    		}
    		for (int i = tot - 1; i >= 1; i--) mn[i] = Min(mn[i], mn[i + 1]);
    		cnt = 0;
    		for (int i = 1; i <= tot; i++)
    		{
    			if (mn[i] > mn[i - 1]) sze[++cnt] = mn[i] - mn[i - 1];
    			mx[i] = cnt;
    		}
    		for (int i = 1; i <= cnt; i++)
    		{
    			C[i][0] = 1;
    			for (int j = 1; j <= tot; j++)
    				C[i][j] = 1ll * C[i][j - 1] * (sze[i] - j + 1)
    					% EI * inv[j] % EI;
    		}
    		memset(g, 0, sizeof(g));
    		for (int i = 0; i <= cnt; i++) g[0][i] = 1;
    		for (int i = 1; i <= tot; i++)
    			for (int j = 1; j <= cnt; j++)
    			{
    				for (int k = i; k >= 1; k--) if (j <= mx[k])
    					g[i][j] = (1ll * g[k - 1][j - 1] * C[j][i - k + 1]
    						+ g[i][j]) % EI;
    				g[i][j] = (g[i][j] + g[i][j - 1]) % EI;
    			}
    		ans = (1ll * g[tot][cnt] * lis + ans) % EI;
    	} while (std::next_permutation(p + 1, p + tot + 1));
    }
    
    void dfs_min(int dep)
    {
    	if (dep == n + 1) return solve();
    	for (int i = 1; i <= tot; i++) bel[dep] = i, dfs_min(dep + 1);
    	bel[dep] = ++tot; dfs_min(dep + 1); tot--;
    }
    
    int qpow(int a, int b)
    {
    	int res = 1;
    	while (b)
    	{
    		if (b & 1) res = 1ll * res * a % EI;
    		a = 1ll * a * a % EI;
    		b >>= 1;
    	}
    	return res;
    }
    
    int main()
    {
    	read(n); inv[1] = 1;
    	for (int i = 1; i <= n; i++) read(a[i]);
    	for (int i = 2; i <= n; i++)
    		inv[i] = 1ll * (EI - EI / i) * inv[EI % i] % EI;
    	dfs_min(1);
    	for (int i = 1; i <= n; i++) ans = 1ll * ans * qpow(a[i], EI - 2) % EI;
    	return std::cout << ans << std::endl, 0;
    }
    

    F

    简要题意

    给定一个 (N) 个数的序列 (H)(H_i) 可以为 ([1,A_i]) 的任何整数。

    定义 (P_i) 表示满足 (j<i)(H_j>H_i) 的最大 (j),如果不存在则 (P_i=-1)

    求有多少种不同的 (P) 数组。(Nle 100)(A_ile10^5)

    Solution

    不难想到笛卡尔树,根为最大数的位置(相同情况下取最右位置)。可以注意到根即为最后一个 (-1) 的位置,这个位置上的数要顶到最大值。

    于是定义 DP:(f_{l,r,i}) 表示区间 ([l,r]) 内,强制所有的数不超过 (i)(P) 数组的取值方案数。

    转移:(f_{l,r,i}leftarrow f_{l,mid-1,min(i,A_{mid})}+f_{mid+1,r,min(i,A_{mid})-1})

    可以发现为了维持 (H) 数组一种特定的相互大小关系,在最坏情况下 (H) 数组的最大值不超过 (N),所以第三维可以只记录前 (O(N)) 个。(O(N^4))

    Code

    #include <bits/stdc++.h>
    
    template <class T>
    inline void read(T &res)
    {
    	res = 0; bool bo = 0; char c;
    	while (((c = getchar()) < '0' || c > '9') && c != '-');
    	if (c == '-') bo = 1; else res = c - 48;
    	while ((c = getchar()) >= '0' && c <= '9')
    		res = (res << 3) + (res << 1) + (c - 48);
    	if (bo) res = ~res + 1;
    }
    
    const int N = 110, M = 210, EI = 1e9 + 7;
    
    int n, a[N], f[N][N][M];
    
    int main()
    {
    	read(n);
    	for (int i = 1; i <= n; i++)
    	{
    		read(a[i]);
    		for (int j = 1; j <= 201; j++) f[i][i][j] = 1;
    	}
    	for (int l = n - 1; l >= 1; l--)
    		for (int r = l + 1; r <= n; r++)
    		{
    			for (int j = 1; j <= 201; j++)
    				for (int mid = l; mid <= r; mid++)
    				{
    					int x = j, delta = 1; if (a[mid] < x) x = a[mid];
    					if (l < mid) delta = 1ll * delta * f[l][mid - 1][x] % EI;
    					if (mid < r) delta = 1ll * delta *
    						f[mid + 1][r][x <= 200 ? x - 1 : x] % EI;
    					if ((f[l][r][j] += delta) >= EI)
    						f[l][r][j] -= EI;
    				}
    		}
    	return std::cout << f[1][n][201] << std::endl, 0;
    }
    
  • 相关阅读:
    多条件搜索问题 -sql拼接与参数化查询
    MVC View中获取action、controller、area名称、参数
    Hadoop权限认证的执行流程
    Java API操作HA方式下的Hadoop
    利用HBase的快照功能来修改表名
    hive两大表关联优化试验
    Spark SQL与Hive on Spark的比较
    Spark的RDD原理以及2.0特性的介绍
    hbase Java API 介绍及使用示例
    初识Spark2.0之Spark SQL
  • 原文地址:https://www.cnblogs.com/xyz32768/p/13778393.html
Copyright © 2011-2022 走看看