ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

2022.5.2 比赛题整理

2022-07-20 08:35:30  阅读:191  来源: 互联网

标签:return 比赛 int dif up maxn 整理 2022.5 扩充


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}\)。

  • 若 \(i + d < j\),例如:

    123456789
    abaaababb
    i   j
    

    此时 \(i = 1,\ j = 5,\ d = 3,\ n = 9\),可以发现 \(k\) 的取值范围就是 8~9。

    我们由此可以得出:当 \(s[i+d]<s[j+d]\) 时,\(f_{i,j}=sum_{j,j + d + 1}\);否则,\(f_{i,j}\) 为 0。

  • 若 \(i + d \geq j\),例如:

    123456789
    abaabaabb
    i  j 
    

    此时 \(i = 1,\ j = 4,\ d = 5,\ n = 9\),我们还发现 \(s[i,j - 1]\) 与 \(s[j,j+(j - i) - 1]\) 完全相同。所以为保证字典序严格递增,可以得到 \(k\) 的取值范围就是 8~9。

    我们由此可以得出:当 \(j+(j-i)\leq n\) 时,\(f_{i,j} = sum_{j,j+(j-i)+1}\)。

这样就得到 \(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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有