这场昨天没报名,今天早上爬起来按惯例补了四题。
感觉奇奇怪怪的构造题比较多。尤其是C这种题,我基本碰到就等于结束了。
膜昨天比赛光速出了四题的大哥%%%
这第一题就卡了我一会儿。
会发现前面所有的数加起来都没最后一个数大,然后我们把最后一个数和前 n 2 − 1 {n \over 2} -1 2n−1个数放在一起,把剩下的放在一起,模拟一下就好了。
#include
using namespace std;
typedef long long ll;
ll pow(int b)
{
ll ans = 1;
for (int i = 1; i <= b; ++i) ans *= 2;
return ans;
}
int main()
{
int t; cin >> t;
while (t--)
{
int n; cin >> n;
ll sum1 = 0, sum2 = 0;
for (int i = 1; i < n / 2; ++i) sum1 += pow(i);
sum1 += pow(n);
for (int i = n / 2; i < n; ++i) sum2 += pow(i);
cout << sum1 - sum2 << endl;
}
return 0;
}
这题只是看起来吓人。因为题目没要求最短序列,所以我们考虑构造一个就可以了。
由题意很快知道这是个周期为 k k k的数列。然后把所有数字去个重,如果不同的数字个数都大于 k k k了肯定构造不出来。剩下的数字我们把它用1补成长度为 k k k的数列,然后输出 n n n次。这样 n n n次循环中每次都能找到一个对应于原数列当前位置的数。
#include
using namespace std;
typedef long long ll;
int main()
{
int t; cin >> t;
while (t--)
{
int n, k; cin >> n >> k;
int a[110]; set <int> st;
for (int i = 1; i <= n; ++i)
{
cin >> a[i]; st.insert(a[i]);
}
if (k < st.size()) cout << -1 << endl;
else
{
cout << n * k << endl;
for (int i = 1; i <= n; ++i)
{
for (auto &j : st) cout << j << ' ';
for (int j = st.size() + 1; j <= k; ++j) cout << 1 << ' ';
}
cout << endl;
}
}
return 0;
}
个人觉得这题很难…自己没做出来。
考的其实是个分类讨论,不过怎么讨论就很玄学。
我们先读个字符串进来排个序。
如果 s [ 0 ] ! = s [ k − 1 ] s[0]!=s[k-1] s[0]!=s[k−1],会发现这时最小就是 s [ k − 1 ] s[k-1] s[k−1]。因为要分成 k k k个串,每个串不为空,那么每个串至少都要往里面塞一个字符。然后一旦前 k k k个元素不等,那塞一个字符所能得到最大串就是 s [ k − 1 ] s[k-1] s[k−1]。之后不管我们对剩下的字符怎么操作,新生成的最大的串 x x x一定有 x ≥ s [ k − 1 ] x\ge s[k-1] x≥s[k−1]。那么就可以把之后的所有元素塞到 s [ 0 ] s[0] s[0]里面,这样依旧保证了 s [ 0 ] < s [ k − 1 ] s[0]s[0]<s[k−1],而且使最大的最小。
如果前面的 k k k个元素都相等,我们看剩下来的元素。如果剩下来的元素都相等,那么显然平均分是最优的。如果剩下的元素有不等的,这时平均分就不一定最优了。比如 a a a b b c , k = 3 aaabbc,k=3 aaabbc,k=3,平均分是 a b , a b , a c ab,ab,ac ab,ab,ac,而 a b b c abbc abbc这时是最优的。
这时我们考虑剩下的元素里最大的字符 c c c,而如果 c c c加到了一些字符串里,那这些串中的某一个一定会成为最大的。那既然这样,我们干脆使包含字符 c c c的字符串最小,所以我们直接把剩下的元素全部塞到 s [ 0 ] s[0] s[0]后面就行了。可以感性理解一下这时一定是最小的。
不过这个我自己真的想不到啊…
代码很短。
#include
using namespace std;
typedef long long ll;
int main()
{
int t; cin >> t;
while (t--)
{
int n, k; cin >> n >> k;
string x, ans = ""; cin >> x;
sort(x.begin(), x.end());
if (x[0] != x[k - 1]) cout << x[k - 1] << endl;
else
{
cout << x[k - 1];
if (x[k] == x[n - 1])
for (int i = 1; i <= ceil((double)(n - k) / k); ++i) cout << x[n - 1];
else
for (int i = k; i < n; ++i) cout << x[i];
cout << endl;
}
}
return 0;
}
这题我就先把我的代码贴出来吧。因为写得太丑了。而且构造方法也非常混乱,我都不知道怎么就过了…
#include
using namespace std;
typedef long long ll;
ll qpow(int a, int b)
{
ll ans = 1;
while (b)
{
if (b & 1) ans *= a;
a *= a, b >>= 1;
}
return ans;
}
int main()
{
int t; cin >> t;
while (t--)
{
int n; cin >> n;
int ans = 1, sum = 1, cnt = 0;
while (1)
{
cnt++, sum += qpow(2, cnt - 1);
ans += sum;
if (ans >= n) break;
}
cout << cnt << endl;
vector <ll> temp; ll prev = 1, sav = cnt + 1;
for (int i = 1; i <= cnt; ++i)
{
temp.push_back(prev);
temp[i - 1] = min(prev, (n - sav) / (cnt - i + 1));
prev += temp[i - 1]; sav += (cnt - i + 1) * temp[i - 1];
}
for (auto &i : temp) cout << i << ' ';
cout << endl;
}
return 0;
}
这题的标答比较有参考价值,就直接说标答了。
看一下这题的输出是个差分数组。差分数组和原数组是可以互相转化的。于是考虑构造原数组 a [ ] a[] a[]。
设第 i i i天的细菌个数是 a [ i ] a[i] a[i],会发现这堆细菌如果分裂的话最多只会变成两倍,并且最少——就是不分裂——也和前一天的细菌个数相同。所以有这样的关系: a [ i ] ≤ a [ i + 1 ] ≤ 2 a [ i ] a[i]\le a[i+1] \le 2a[i] a[i]≤a[i+1]≤2a[i]。而一天增加的重量就是细菌的个数。因此如果定义 a [ 0 ] = 1 a[0]=1 a[0]=1,那么到第 x x x天细菌的总重量就是 Σ i = 0 x a [ i ] \Sigma_{i=0}^{x}a[i] Σi=0xa[i]。因为要求天数最少,我们就可以先用最高速分裂,就是 a [ i + 1 ] = 2 a [ i ] a[i+1]=2a[i] a[i+1]=2a[i]。如果刚好有一天总重量和要求的重量相等了,那我们就构造好了这样的一个数组。如果有一天总重量大于要求的重量了,那我们就把最后一次分裂产生的数删掉。此时若设前面的和为 s s s,那我们就在这个数组后面塞个 n − s n-s n−s,之后排个序。这样同样也得到了一个数组。
之后只要输出相邻两个元素的差就可以了。
这题的思路是当差分数组比较难构造时,可以尝试构造原数组,最后可以通过前缀和或者作差相互转化。
我就是一直在构造差分数组然后浪费1h…
#include
using namespace std;
typedef long long ll;
int main()
{
int t; cin >> t;
while (t--)
{
int n; cin >> n;
vector <ll> ans; ll sum = 0;
ans.push_back(1), sum = 1;
while (sum < n) ans.push_back(1 << ans.size()), sum = (1 << ans.size()) - 1;
if (sum > n) ans.pop_back(), ans.push_back(n - ((1 << ans.size()) - 1));
sort(ans.begin(), ans.end());
cout << ans.size() - 1 << endl;
for (int i = 1; i < ans.size(); ++i) cout << ans[i] - ans[i - 1] << ' ';
cout << endl;
}
return 0;
}
最后用正解的算法写出来的程序也很短。