A题:小水獭游河南
题意:给定一个字符串,判断是否由一个只含只由单个字母组成的字符串和一个回文串前后拼接而成
Solution
将只由单个字母组成的字符串称为,回文串称为,由于最多只有26个单个英文字母,所以
。对给定的字符串从前往后开始枚举,每次枚举判断后面的字符串是否构成回文串,枚举的次数一定小于27,时间复杂度为线性级别
Code
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
#define int long long
string s;
int sv(int x)
{
for (int i = x + 1; i <= (s.size() + x) / 2; i++)
if (s[i] != s[s.size() + x - i])return 0;
return 1;
}
signed main()
{
ios::sync_with_stdio(0); cout.tie(0); cin.tie(0);
int t; cin >> t;
while (t--)
{
unordered_map<char, int>a;
cin >> s;
if(s.size()==1)注意特判size()=1
{
cout<<"NaN"<<endl;
continue;
}
for (int i = 0; i < s.size(); i++)
{
if (a[s[i]] == 0)
{
if (sv(i))
{
cout << "HE" << endl;
break;
}
a[s[i]]++;
}
else//如果出现了a[s[i]]==1,说明前面的所有串都不能构成正确的S1,则一定不存在
{
cout << "NaN" << endl;
break;
}
}
}
return 0;
}
H题:Travel Begins
题意:给出两个正整数N,K,将数字N分成K个非负实数,K个实数若存在小数且小数部分>=0.5,则对答案贡献向上取整,否则向下取整,问K个实数贡献求和所能取得的最小和最大值
Solution
1.MAX
初始时K个数字为0,考虑这样一个事实:若分给一个数字0.5,则其会变成1,而后只能给他1及以上的值,其才能变为2,对答案贡献(2-1(1是其原来的贡献)=1)而若将给他的1变为2个0.5,交给2个为0的值,对答案贡献为2-0=2,所以我们先给是0的地方0.5,使其贡献为1,若没有剩余,则所有数加起来之和就是最大贡献,若有剩余,对于任意一个数字,给他1的倍数是最划算的。
2.MIN
初始时K个数字为0,考虑这样一个事实:若分给一个数字0.499999999......,对答案贡献仍是0,
我们先对每个数字分0.4999999....,若分到最后没有剩余,则答案为0,若有剩余,则将其分给任意一个数字,且它的小数部分加上0.4999999...一定大于0.5,所以对剩余的值向上取整即可
注意:不能使用ceil和floor,会有精度丢失(wa了5发得来的教训。。。)
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main()
{
ios::sync_with_stdio(0); cout.tie(0); cin.tie(0);
int t; cin >> t;
while (t--)
{
int n, k; cin >> n >> k;
if (n * 2 < k)cout << 0 << " ";
else
{
cout << (int)(n - 0.4999999999999 * k+1) << " ";
}
if (n * 2 <= k)
cout << n * 2 << endl;
else cout << k + (int)(n - 0.5 * k) << endl;
}
return 0;
}
F题:Art for Last
题意:给出一个长为n的非负整数序列和数字k,要求从序列中选出k()个数字使得 任意两项作差的绝对值的最小值*任意两项作差的绝对值的最大值 最小
Solution
1.作差的绝对值的最小值
若想使得该值最小,可以想到一定是长度为k的序列中相差最近的两个值的差
2.作差的绝对值的最大值
若想使得该值最小,也可以想到,当长度为k的序列中最大的值与序列中最小的值相差最近时。,该值最小
所以对序列进行排序,从前往后开始枚举,最大值可直接求得,但最小值每次枚举需要遍历一次长度为k的序列,必然超时,考虑维持原序列的差分数组CF,若要求序列 的作差最小值,则是求差分数组中的最小值,这里可用滑动窗口求最小值,也可以用线段树维护区间最小值,下面给出代码为线段树
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e5 + 10;
int a[N];
int cf[N];
struct node {
int l, r, num, min;
}tr[N * 4];
void pushup(int u)
{
tr[u].min = min(tr[u << 1].min, tr[u << 1 | 1].min);
}
void build(int u, int l, int r)
{
if (l == r)
{
tr[u] = { l,l,cf[l],cf[l] };
return;
}
else
{
tr[u] = { l,r };
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
return;
}
}
int query(int u, int l, int r)
{
if (l <= tr[u].l && r >= tr[u].r)return tr[u].min;
else
{
int mid = tr[u].l + tr[u].r >> 1;
int min1 = 1e9;
if (l <= mid)min1 = query(u << 1, l, r);
if (r > mid)min1 = min(min1, query(u << 1 | 1, l, r));
return min1;
}
}
signed main()
{
ios::sync_with_stdio(0); cout.tie(0); cin.tie(0);
int n, k; cin >> n >> k;
for (int i = 1; i <= n; i++)cin >> a[i];
sort(a + 1, a + 1 + n);
for (int i = 1; i <= n; i++)cf[i] = a[i] - a[i - 1];
build(1, 2, n);
int min1 = 1e18;
for (int i = k; i <= n; i++)
{
min1 = min(min1, (a[i]-a[i-k+1]) * query(1, i - k + 2, i));
}
cout << min1;
return 0;
}
B题:Art for Rest
题意:给出一个长度为n的序列,每次将序列按顺序分成多端,每段长度为K,每次对每段从小到大进行排序,问存在多少个使得序列单调不减
Solution
首先想到线段树维护区间最值,只要每段的最大值小于等于下一段的最小值即可,但n过大,k从1到n,每次查询n/k,达到n方级别
正确解法:维护前缀最大值和后缀最小值,每次我们只需要考虑前缀最大值是否大于后缀最小值即可
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 10;
int pre[N], back[N];
int a[N];
signed main()
{
ios::sync_with_stdio(0); cout.tie(0); cin.tie(0);
int n; cin >> n;
for (int i = 1; i <= n; i++)cin >> a[i];
pre[1] = a[1], back[n] = a[n];
for (int i = 2; i <= n; i++)pre[i] = max(pre[i - 1], a[i]);
for (int i = n - 1; i >= 1; i--)back[i] = min(back[i + 1], a[i]);
int cnt = 0;
for (int k = 1; k <= n; k++)
{
int p = 0;
for (int i = 1; i <= n; i += k)
{
int j = i + k - 1;
if(j+1>n)break;
if (pre[j] > back[j+1])
{
p = 1;
break;
}
}
if (!p)++cnt;
}
cout << cnt;
return 0;
}