A - Weird Function
给出f函数表达式,求:f(f(f(t)+t)+f(f(t)))签到题
重复利用的代码,最好写一个函数出来
#include <bits/stdc++.h>
using namespace std;
int f(int x)
{
return x * x + 2 * x + 3;
}
int main() {
int t;
cin >> t;
int ans = 0;
int ft = f(t);
int ftt = ft + t;
int fftt = f(ftt);
int fft = f(ft);
ans = f(fftt + fft);
cout << ans << endl;
return 0;
}
B - Longest Segment
给出n个点,找出两点间最长距离签到题
#include <bits/stdc++.h>
using namespace std;
double f(double x1, double y1, double x2, double y2)
{
return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
}
int main() {
int n;
cin >> n;
vector<pair<double, double>> vec(n);
double ans = 0;
for(int i = 0; i < n; i ++)
{
double x1, y1;
cin >> x1 >> y1;
vec[i] = {x1, y1};
}
for(int i = 0; i < n; i ++)
{
for(int j = i + 1; j < n; j ++)
{
double temp = f(vec[i].first, vec[i].second, vec[j].first, vec[j].second);
if(temp - ans > 1e-18) ans = temp;
}
}
printf("%.10f\n", sqrt(ans));
return 0;
}
C - Happy New Year!
求第k个仅由0和2组成的十进制数字。二进制模拟
此题中:十进制数字中,填写2的可以看成二进制中的1, 填写0的可以看成二进制中的0。 这样每一个仅由0、2组成的十进制数字均唯一对应一个二进制数字。在此规则下,把该十进制转化为相应的二进制表示,逆序输出即可。(大小关系不变)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
LL n;
cin >> n;
vector<int> vec;
while(n)
{
if(n & 1) vec.push_back(2);
else vec.push_back(0);
n >>= 1;
}
for(int i = vec.size() - 1; i >= 0; i --)
{
printf("%d", vec[i]);
}
return 0;
}
D - Prefix K-th Max
题目大意:给出一个排列P, 从前i个数中,找到第k大的数(i >= k时)。STL应用
此处给出两种写法 堆、set
堆(优先队列版本)
思路:
小根堆,保证堆顶为堆中最小的元素。我们维护堆中的元素为k个,堆顶的即为所求。即堆中一定有 k - 1个大于堆顶的元素,堆顶即为第k大的元素
//从前i个数中,找到第k大的数(i >= k时)
#include <bits/stdc++.h>
using namespace std;
int main(){
int n, k;
scanf("%d%d", &n, &k);
priority_queue<int, vector<int>, greater<int>> que; //小根堆
for(int i = 1; i <= n; i ++)
{
int num;
scanf("%d", &num);
que.push(num);
if(i == k)
{
printf("%d\n", que.top());
}
else if(i > k)
{
que.pop();
printf("%d\n", que.top());
}
}
}
set版本
set的特性:去重、自动排序(默认从小到大)
- 当i <= k时,我们直接插入num, i == k时,直接输出set头元素。
- 当i > k时, set中第一个元素last(为前i个的第k大),如果num > last, 则将last删除,并将num插入set中。set中的第一个元素仍为第k大。
//从前i个数中,找到第k大的数(i >= k时)
#include <bits/stdc++.h>
using namespace std;
int main(){
int n, k;
scanf("%d%d", &n, &k);
set<int> st;
for(int i = 1; i <= n; i ++)
{
int num;
scanf("%d", &num);
if(i <= k) st.insert(num);
if(i == k)
{
printf("%d\n", *st.begin());
}
else if(i > k)
{
auto last = *st.begin();
if(num > last){
st.insert(num);
st.erase(last);
}
printf("%d\n", *st.begin());
}
}
}
E - Arithmetic Number
题目大意:当一个十进制数字满足:d[2] - d[1] = d[3] - d[2] = …= d[n] - d[n - 1]时(一共由n位数字组成,从低位到高位以此为d[1] …到d[n]),就是一个arithmetic number。
给出x, 求不小于x的最小的arithmetic number。思维、代码实现
思路:
- 十进制数字由0-9组成,且x < 10 ^ 17, 不多于18位。直接枚举d[n] - d[n - 1]的值,最高位的数字,最多18位,即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main(){
LL n;
cin >> n;
LL ans = 1e18;
for(int i = 0; i < 10; i ++)
{
for(int j = 0; j < 10; j ++)
{
int d = j - i, temp = i; //两者之间的差值,前一个数
LL y = 0;
for(int k = 0; k < 18; k ++)
{
if(temp < 0 || temp > 9){
break;
}
y = y * 10 + temp;
temp += d;
if(y >= n) ans = min(ans, y);
}
}
}
cout << ans <<endl;
}
F - Reordering
题目大意:
S是由小写字母组成的长度为[1, 5000]的字符串,求能从S中得到多少非空子串(此题中字母出现的顺序可以和S不一样)。组合数、dp
分析:
- 没有思路时,观察样例,一个字串可以包含两部分:组成的子串包含的字母;以及各个字母所用的数量。由此我们可以想到dp
- 状态表示:dp[i,j]:从前i个字母中选(1 <= i <= 26),组成长度为j的方案数
- 状态计算:
(freqi:表示 i + ‘a’ 这个字母出现了多少次。)
即:从前i - 1个字母中选,选择长度为(j - k)的方案数(即dp[i-1][j-k]
) * 从j个位置中选择k个该字母的位置方法数(即g数组). 再求和(0 <= k <= min(j, freqi))
思考:从i个位置中选择j个位置该怎么求?
(1)第i个位置选, 则从前i-1中选j个。即g[i-1][j-1]
(2)第i个位置不选:即g[i-1][j]
所以 g[i][j] = g[i-1][j-1]+g[i-1][j]
代码如下:
- 解释sum数组:从前i个字母中选,总长度最大为多少。
- 解释第三重循环:当我枚举选择第三个字母的数量时,会超时;而枚举前i-1个选择的数量时,不会超时。(会卡aaaaaaaaaa这样的数据)
#include <bits/stdc++.h>
using namespace std;
const int N = 5010, mod = 998244353;
typedef long long LL;
int g[N][N];
char str[N];
int cnt[30];
int sum[30]; //前i个字母一共有多少个
int dp[30][N];
int main(void)
{
cin >> str;
int n = strlen(str);
//预处理g数组
for(int i = 0; i <= n; i ++)
{
g[i][0] = 1;
for(int j = 1; j <= i; j ++)
{
g[i][j] = (g[i - 1][j] + g[i - 1][j - 1]) % mod;
}
}
//处理cnt数组,每个字母有多少个
for(int i = 0; i < n; i ++)
{
cnt[str[i] - 'a' + 1] ++;
}
for(int i = 1; i <= 26; i ++) sum[i] = sum[i-1] + cnt[i];
dp[0][0] = 1;
for(int i = 1; i <= 26; i ++)
{
for(int j = 0; j <= sum[i]; j ++) //总长度
{
/*
for(int k = 0; k <= min(cnt[i], sum[i]); k ++) //选择第i个字母的个数
{
dp[i][j] = (dp[i][j] + (LL) dp[i - 1][j - k] * g[j][k]) % mod;
}
*/
for(int k = 0; k <= min(j, sum[i - 1]); k ++) //前i-1个字母组成的长度
{
if(j - k <= cnt[i]) dp[i][j] = (dp[i][j] + (LL)dp[i - 1][k] * g[j][j - k]) % mod;
}
}
}
LL ans = 0;
for(int i = 1; i <= sum[26]; i ++)
{
ans = (ans + dp[26][i]) % mod;
}
cout << ans <<endl;
return 0;
}