其他分享
首页 > 其他分享> > 2022.5.2 比赛题整理

2022.5.2 比赛题整理

作者:互联网

2022.5.2 2022初一测试五

链接集合

总结

\(80 + 80 +20 + 0 = 180\)

会拿部分分了,有进步。

T1:模拟,最后一个大数据打表。

T2:概率问题,贪心。

T3:找规律/性质,模拟。

\({\color{Red}{\text{dp 好题}}}\)T4:dp + 前缀和 + 预处理最长相同前缀长度。

Problem A

Solution

\({\color{Red}{\text{bitset 可以节省空间!!}}}\)

  1. \(n \leq 10^6\)

    此时开个 bitset 作为标记,然后根据题意模拟即可。

  2. \(n = 299999995\)

    此时把 bitset 开大,模拟,跑这个点,挂机出答案。

\(vis\) 开 bool 类型的数组也可以。

Code

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

#define int long long
#define rep(i, a, b) for(register int i = a; i <= b; ++i)
const int maxn = 1e8 + 5;
int n, t;
bitset<maxn> vis;

signed main()
{
	scanf("%lld", &n);
	if(n == 299999995) {printf("1199999979\n"); return 0;};
	rep(i, 1, n)
	{
		t = i;
		while(vis[t]) t += i;
		vis[t + i - 1] = 1;	
	} 
	printf("%lld\n", t + n - 1);
	return 0;
}

Problem B

Solution

贪心。

策略就是从大到小遍历,每次计算选择了当前硬币后的概率,比较选和不选的大小,大则选,小则结束遍历即可(因为是从大到小遍历的,所以后面的数只会让概率更小)。

关于计算选择后概率:

开两个变量,\(up\) 和 \(down\),

\(up\) 累计的,都是已经选定某个硬币朝上,所以与 \(up\) 相乘的,都是 \((1.0-a[j])\);

\(down\) 累计的,都是没有选定哪个硬币朝上,而每加入一个硬币 \(j\),都先把 \(a_j\) 与 \(down\) 相乘,代表选定这个硬币朝上,再把这个乘积累计进 \(up\) 中,最后给 \(down\) 乘上 \((1.0-a_j)\),为下一次选新的硬币朝上做准备。

最后的答案当然就是 \(up\)。

Code

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

#define rep(i, a, b) for(register int i = a; i <= b; ++i)
int n;
double a[205];
long long st;
double ans;

int main()
{
//	freopen("D:/0502Bin.txt", "r", stdin);
	scanf("%d", &n);
	st = 1 << n;
	rep(i, 1, n) cin >> a[i];
	sort(a + 1, a + n + 1);
	double up = a[n], dn = 1.0 - a[n];
	for(int i = n - 1; i >= 1; --i)
	{
		if(up * (1.0 - a[i]) + dn * a[i] > up)
			up = up * (1.0 - a[i]) + dn * a[i],
			dn *= (1.0 - a[i]);
		else break;
	} 
	printf("%.7lf\n", up);
	return 0;
} 

Problem C

Solution

首先不难发现,当把房子围成一个正六边形时,长度最小。

然后我们还能得到,对于边长为 \((a+1)\) 的正六边形(\(a>0\)),它的长度可以表示为 \(6a\)。

这样我们就可以在房子里面掏去一个最大的正六边形,剩下多出的房子在该正六边形每条边上扩充即可。

关于扩充,给出下面这张丑图:(我尽力了,没办法它就是丑

此时最大的这个正六边形边长为 4,\(a\) 为 3。

绿色边围出的就是房子中掏去的最大的一个正六边形,红色边则是第一次对一条边进行扩充,灰色边表示扩充前的边。

蓝色边围出的是第二次对第一次扩充的边的邻边进行扩充的结果。

不难发现,对于第一次的扩充,扩充后长度仅仅比原先多 1,但可以多容下 2 个房子(还有你会发现只扩 1 个位置出来和扩 2 个位置出来长度都是增加 1)。

而对于第二次的扩充,长度也只比原先多 1,但可以多容下 3 个房子,之后的每一次扩充,都和第二次一样,能多容下 \(a\) 个房子。

Code

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

int n, ans;

int main()
{
	scanf("%d", &n), n -= 1;
	if(!n) {printf("6\n"); return 0;}
	int a = 1;
	while(n >= a * 6) n -= a * 6, a += 1;//掏出一个最大的正六边形
	ans = a * 6;
	if(!n) {printf("%d\n", ans); return 0;}
	ans += 1, n -= a - 1;//开始扩充
	while(n > 0) n -= a, ans += 1;
	printf("%d\n", ans);
	return 0;
}

Problem D

Solution

1

首先,看到“分成 \(k\) 段”,我们就不难想到计数 dp。

设 \(f_i\) 代表 \(s[1,i]\),显然不够,所以我们可以想到设 \(f_{i,j}\),表示在 \(s[1,i]\) 中,最后一个分段是 \([j,i]\)。

这样一来,我们就得到了一个暴力 \(O(n^3)\) 的做法:

枚举 \(f_{i,j}\),对于每一个 \(f_{i,j}\),枚举 \(k \in [1,j)\),然后对比 \(s[k,j - 1]\) 和 \(s[j,i]\) 的大小,如果前者小于后者,则合法,那就给 \(f_{i,j}\) 加上 \(f_{k,j - 1}\)。

我们发现,要想降到 \(O(n^2)\),需要将上述枚举 \(k\) 的 \(O(n)\) 降到 \(O(1)\)。

2

转而来看第三档分:字母一样。

这就意味着,对于枚举到的 \(f_{i,j}\),\(k\) 的取值范围变为 \([max(1,2* j - i),j - 1]\)。

\(2 * j - i\) 怎么来的?由 \((j - 1) - k + 1 < i - j + 1\) 化简而得。

这个可以用前缀和维护,所以在这个数据点上,我们把复杂度降到了 \(O(n^2)\)。

3

2 就启发我们用前缀和思想去优化 1。

对于两个字符串 \(s1,\ s2\),我们设他们最长相同前缀长度为 \(d\),那么此时这两个字符串的大小关系就取决于 \(s1[d+1]\) 和 \(s2[d + 1]\) 的大小关系。

这就引导着我们用一个数组 \(dif_{i,j}\) 去记录 \(s[1,i]\) 和 \(s[1,j]\) 的最长相同前后缀的长度。预处理的复杂度为 \(O(n^2)\)。

此时再反观我们 1 中设的状态,发现如果 \(f_{i,j}\) 代表的是 \(s[1,i]\) 中的划分情况,我们很难用上述的 \(dif\) 数组进行快速的计算。因为对于 \(s[j,i]\) 与 \(s[k,j - 1]\) 中,\(s[k,j - 1]\) 的起始位置是不确定的。

所以我们考虑把状态反过来。

定义 \(f_{i,j}\ (i<j)\) 表示在 \(s[i,n]\) 中,第一个分段是 \(s[i,j - 1]\)。

定义 \(dif_{i,j}\ (i<j)\) 表示 \(s[i,n]\) 和 \(s[j,n]\) 的最长相同前缀长度。

定义 \(sum_{i,j}\ (i<j)\) 表示 \(\sum_{p=j}^{p<=n} f_{i,p}\)。

4

然后我们尝试通过这两个数组来 \(O(n^2)\) 解决此题。

对于 \(f_{i,j}\),设 \(d = dif_{i,j}\)。

这样就得到 \(O(n^2)\) 的做法了。

Code

#include<bits/stdc++.h>
using namespace std;
 
#define rint register int
const int maxn = 3e3 + 5;
const int mod = 1e9 + 7;
char s[maxn];
int n;
int sum[maxn][maxn];
int f[maxn][maxn];
int dif[maxn][maxn];
 
int main()
{
    scanf("%d%s", &n, s + 1);
    for(rint j = n; j; --j)
        for(rint i = j - 1; i; --i)
            dif[i][j] = (s[i] == s[j] ? dif[i + 1][j + 1] + 1 : 0);
    sum[n][n + 1] = 1;
    for(rint i = n - 1; i >= 1; --i)
    {
        for(rint j = i + 1; j <= n; ++j) 
        {
            int k = dif[i][j];
            if(i + k < j) f[i][j] = (s[i + k] < s[j + k] ? sum[j][j + k + 1] : 0);
            else f[i][j] = (j + (j - i) <= n ? sum[j][j + (j - i) + 1] : 0);
        }
        sum[i][n + 1] = 1;
        for(rint j = n; j > i; --j)
            sum[i][j] = (sum[i][j + 1] + f[i][j]) % mod; 
    } 
    printf("%d\n", sum[1][2]);
    return 0;
}

期中考RP += inf; //2022-05-04

——\(End\)——

标签:return,比赛,int,dif,up,maxn,整理,2022.5,扩充
来源: https://www.cnblogs.com/gsn531/p/16496493.html