1.29 educational round 81
1.29 Educational Codeforces Round 81
A Display The Number
题目描述
数字屏上0-9数字点亮分别需要不同的段、给定一定段,问能够点亮的最大的数字是几
题解
对于偶数段 ,则最大数字的每一位都是1就可以,对于奇数段,令第一位数字是7,之后全都是1即可
B Infinite Prefixes
题目描述
有一个长度为n的01串,可以无限次叠加,定义一个前缀01差:前\(i\)项中0比1多的个数,问有多少个前缀01差等于\(m\)
题解
每一个串存在一个\(u\),即整串的前缀01差,无限前缀能组成的数字仅有前\(n\)项前缀01差及无限u的叠加
(注意考虑特殊条件) 不考虑就hack
C Obtain The String
题目描述
有两个小写字母字符串\(s\)和\(t\),定义操作:每次可将s的一个非连续子串取出,求最小的操作数\(n\),可使得\(n\)次得到的子串连续成为字符串\(t\)
题解
直接贪心,利用子序列自动机加速
将26个字母保存1个\(nex\)二维数组,\(nex_{ij}\)表示第\(s\)在第\(i\)个位置后的字母$j $第一次出现的位置,我们在求解过程中:
首先查找t的第一个字母在s中第一次出现的位置,即\(nex_{0,t_1}\),找到第一个字母后,寻找t的第二个字母的过程就是在\(s\)中\(t\)的第一个字母的位置后继续寻找第一次出现第二个字母的位置,以此类推,若是找不到,则操作数++,重新寻找
#include<bits/stdc++.h>
using namespace std;
int T;
char s[100005],t[100005];
int nxt[100005][26];
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%s",s+1);
scanf("%s",t+1);
int n=strlen(s+1),m=strlen(t+1);
for(int j=0;j<26;++j)nxt[n][j]=0;
for(int i=n-1;i>=0;--i)
{
for(int j=0;j<26;++j)nxt[i][j]=nxt[i+1][j];
nxt[i][s[i+1]-'a']=i+1;
}
int now=0,ans=1;
for(int i=1;i<=m;++i)
{
now=nxt[now][t[i]-'a'];
if(!now)
{
ans++;
now=nxt[now][t[i]-'a'];
if(!now){ans=-1;break;}
}
}
printf("%d\n",ans);
}
return 0;
}
D Same GCDs
题目描述
给定\(a,m\),计算$\sum\limits_{i = 0}^{m - 1} {[\gcd (a,m) = = \gcd (a + i,m)]} $
题解
\(gcd (a,m) = d \to \gcd (\frac{a}{d},\frac{m}{d}) = 1\)
则原式可化为
直接求欧拉函数即可
E Permutation Separation
题目描述
给一个排列,每个排列中的第\(i\)个数字为\(p_i\),权值为\(w_i\).你可以选择一个位置切一刀把它分成左右两部分,然后你可以花费\(w_i\)把\(p_i\)移动到另一个部分。最终要保证左半部分的最大值小于右半部分的最小值。
题解
基础还是太弱,看了半天题解感觉终于明白了
首先暴力解法:就是枚举将左右集合分开的位置,然后枚举边界x,使得左边集合大于x的数与右边集合小于x的数花费和最小
线段树解法:
线段树是将枚举的边界x作为叶子节点的
如果能够确定切的位置 左边集合与右边集合均已经确定,每一个叶子节点x保存的是当前集合下以x为边界所需花费,由于线段树维护的是最小值,全局最小值就是当前确定集合的最小花费。
如何更新:
当集合发生改变时,比如说左边集合中新增了右边集合的\(y\),权值为\(w\),则当前集合下的最优解法中的以比y小的边界均需要将y移动到右边,区间增加\(w\),比y大的边界则会略去这一步骤,即区间减去\(w\)