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 可以节省空间!!}}}\)
-
\(n \leq 10^6\)
此时开个
bitset
作为标记,然后根据题意模拟即可。 -
\(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