A.We Got Everything Covered!(贪心)
题意:
给你两个正整数nnn和kkk。
找出一个字符串sss,使得所有长度为nnn的字符串都可以用前kkk个小写英文字母作为sss的子序列出现。
如果有多个答案,则输出长度最小的答案。如果仍有多个答案,输出其中任意一个。
注:如果从bbb中删除一些字符(可能为零个)而不改变其余字符的顺序得到aaa,则字符串aaa称为字符串bbb的子串。
分析:
本题解法不唯一,观察样例可以发现,我们只需要顺倒交替输出前kkk个字母共nnn次即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
int t;
cin >> t;
while (t--) {
int n, k;
cin >> n >> k;
string s;
for (int i = 0; i < n; i++) {
for (int j = 0; j < k; j++) {
s += 'a' + j;
}
}
cout << s << endl;
}
return 0;
}
B.A Balanced Problemset?(枚举)
题意:
杰伊成功地创造了一个难度为xxx的问题。
但Yash担心这个问题会让比赛变得非常不平衡,协调员会拒绝接受它。因此,他决定把它分解成一个由nnn个子问题组成的问题集,使得所有子问题的难度都是一个正整数,并且它们的总和等于xxx。
协调人阿列克谢将问题集的平衡定义为问题集中所有子问题难度的GCDGCDGCD。
求如果Yash最佳地选择子问题的难度,他能达到的最大平衡。
分析:
将xxx分成nnn个数,要求在答案尽量大的同时使得所有子问题的难度可以整除答案,那么这个答案就是每个难度的因子,求他们最大的gcdgcdgcd,显然gcdgcdgcd肯定是xxx的因数,且满足gcd×n≤xgcd \times n \le xgcd×n≤x,根号枚举xxx的因数即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
int t;
cin >> t;
while (t--) {
LL x, n;
cin >> x >> n;
LL tmp = 1;
for (LL i = 1; i <= sqrt(x); i++) {
if (x % i == 0) {
LL cnt = x / i;
if (n * i <= x)
tmp = max(tmp, i);
if (n * cnt <= x)
tmp = max(tmp, cnt);
}
}
cout << tmp << endl;
}
return 0;
}
C.Did We Get Everything Covered?(贪心)(Div1-A)
题意:
给你两个整数nnn和kkk以及一个字符串sss。
检查是否所有长度为nnn的字符串都可以用前kkk个小写英文字母组成,并作为sss的子序列出现。如果答案是否定的,还需要输出一个长度为nnn的字符串,该字符串可以用前kkk个小写英文字母组成,但不会作为sss的子序列出现。
如果有多个答案,输出任意一个。
注:如果从bbb中删除一些字符(可能为零)而不改变其余字符的顺序,就可以得到aaa,那么字符串aaa就被称为字符串bbb的子串。
分析:
本题与AAA题类似,我们考虑YESYESYES的情形,即sss一定可以分成nnn份,每份包含所有前kkk个字符,对字符串从头开始遍历,只要kkk个字母都凑齐就作为一组,最后如果组数大于等于nnn,那么YESYESYES,否则NONONO,构造反例时,选取每组的最后一个字母加上最后一组没出现的字母
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int t = 1;
cin >> t;
while (t--) {
int n, k, m;
cin >> n >> k >> m;
string s;
cin >> s;
int num = 0;
int alphanum = 0;
vector<bool> vis(26);
string ans;
for (auto c: s) {
int tmp = c - 'a';
if (tmp < k && !vis[tmp]) {
vis[tmp] = true;
alphanum++;
if (alphanum == k) {
ans += c;
for (int i = 0; i < 26; i++) {
vis[i] = false;
}
alphanum = 0;
num++;
}
}
}
if (num < n) {
cout << "NO" << endl;
char c;
for (int i = 0; i < k; i++) {
if (!vis[i]) {
c = i + 'a';
}
}
while (ans.size() < n) {
ans += c;
}
cout << ans << endl;
} else {
cout << "YES" << endl;
}
}
return 0;
}
D.Good Trip(期望)
题意:
一个班有nnn个孩子,其中mmm对是朋友。第iii对朋友的友谊值为fif_ifi 。
老师要去远足kkk次,每次远足她都会随机、等价、独立地选择一对孩子。如果选择了一对是朋友的孩子,他们的友谊值在以后的所有远足中都会增加111(教师可以多次选择一对孩子)。如果一对孩子不是朋友,那么他们的友谊值为000,且在以后的游览中不会改变。
求所有被选中参加远足的kkk对朋友的友谊值总和的期望值(在被选中时)。可以证明,这个答案总是可以用分数pq\dfrac{p}{q}qp表示,其中ppp和qqq是共整数。计算p⋅q−1 mod (109+7)p\cdot q^{-1} \bmod (10^9+7)p⋅q−1mod(109+7)。
分析:
首先,我们每次出行要从n个人中选取两个人,即Cn2C_{n}^{2}Cn2。
那么,每次选择到我们固定要的一对二人组的概率p=1Cn2p=\frac{1}{C_{n}^{2}}p=Cn21,选不到我们要的二人组的概率为q=1−pq=1-pq=1−p。
有kkk次远足,即有kkk个二人组,从中共选到某一个特定二人组iii次的概率为Cn2×pi×qk−iC_{n}^{2} \times p^i \times q^{k-i}Cn2×pi×qk−i。
如果这个特定二人组不是朋友,那么他们的期望为000
如果这个特定二人组是朋友,且初始友谊值为fif_ifi,那么他们的期望为∑j=0i−1(fa+j)×Cki×pi×qk−i\sum\limits_{j=0}^{i-1}(f_a+j)\times C_{k}^{i}\times p^i \times q^{k-i}j=0∑i−1(fa+j)×Cki×pi×qk−i。即每选到一次后,友谊值都会加111.
题目要求的是所有情况的期望,也就是每个情况的期望的和。
由上述分析可知,不是朋友的二人组期望贡献为000,所以我们只需要将朋友二人组的期望加起来即可。
有mmm个朋友二人组,将他们累加:Cki×pi×qk−i×∑a=1m∑j=0i−1(fa+j)C_{k}^{i}\times p^i \times q^{k-i}\times \sum\limits_{a=1}^{m} \sum\limits_{j=0}^{i-1}(f_a+j)Cki×pi×qk−i×a=1∑mj=0∑i−1(fa+j),化简后发现是一个等差数列,进行高斯求和即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod = 1e9 + 7;
const LL N = 2e5 + 5;
LL f[N], tmp[N];
LL fix(LL a, LL b) {
LL res = 1;
while (b) {
if (b & 1) {
res = res * a % mod;
}
b >>= 1;
a = a * a % mod;
}
return res;
}
void init(int n) {
f[0] = 1;
for (int i = 1; i <= n; i++) {
f[i] = f[i - 1] * i % mod;
}
tmp[n] = fix(f[n], mod - 2);
for (int i = n - 1; i; i--) {
tmp[i] = (i + 1) * tmp[i + 1] % mod;
}
}
int n, m, k;
LL C(LL a, LL b) {
if (a == b || b == 0) {
return 1;
}
return (f[a] * tmp[a - b] % mod) * tmp[b] % mod;
}
LL S(LL l, LL cnt) {
return ((l + (l + (cnt - 1) * m)) * cnt % mod) * tmp[2] % mod;
}
void solve() {
cin >> n >> m >> k;
LL sum = 0;
for (int i = 1; i <= m; i++) {
int a, b, c;
cin >> a >> b >> c;
sum = (sum + c) % mod;
}
LL ans = 0;
LL p = fix(C(n, 2), mod - 2) % mod;
LL q = (((1 - p) % mod) + mod) % mod;
for (int i = 0; i <= k; i++) {
ans = (ans + ((S(sum, i) * (C(k, i) * fix(p, i) % mod) % mod) * fix(q, k - i) % mod)) % mod;
}
cout << ans << endl;
}
int main() {
int t;
init(2e5 + 5);
cin >> t;
while (t--) {
solve();
}
return 0;
}
E.Space Harbour(区间)(Div1-B)
题意:
在一条直线上有nnn个编号为111至nnn的点。最初有mmm个港口。第iii个海港位于XiX_iXi点,其值为ViV_iVi。题目保证在111点和nnn点都有港口。将一艘船从当前位置移动到下一个港口的成本是其左侧最近港口的价值与右侧最近港口距离的乘积。具体来说,如果一艘船已经在一个港口,那么将它移动到下一个港口的成本是000。
有qqq个查询,每个查询都是以下222种类型中的一种:
- 111 xxx vvv -在xxx点添加一个海港,其值为vvv。保证在添加海港之前,xxx点没有海港。
- 222 lll rrr -输出将lll至rrr 点的所有船只移至下一个港口的费用总和。
注意,只需要计算移动船只的费用,而不需要实际移动船只。
分析:
考虑把每两个码头l,rl,rl,r之间的所有花费看作这个连续段单元[l,r][l,r][l,r]的花费,发现对于一对相邻的港口(xi,xi+1)(x_i,x_{i+1})(xi,xi+1),x∈(xi,xi+1)x∈(x_i,x_{i+1})x∈(xi,xi+1)的花费是yi(xi+1−x)y_i(x_{i+1}−x)yi(xi+1−x)。拆开得 yixi+1−yixy_ix_{i+1}−y_ixyixi+1−yix。
考虑用setsetset维护所有港口,这样可以知道一个港口左边和右边的港口的坐标和价值。那么前一项yixi+1y_ix_{i+1}yixi+1可以线段树区间覆盖,后一项−yix−y_ix−yix也可以线段树区间覆盖处理,相当于让一个代表[l,r][l,r][l,r]区间的线段树结点的和变成−yi(l+r)(r−l+1)2−y_i\frac{(l+r)(r−l+1)}{2}−yi2(l+r)(r−l+1)。这个标记是可以下传的。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod = 998244353;
const LL N = 5e5;
const int MAXN = 300005;
LL n, m, q, a[MAXN], b[MAXN];
inline LL calc(LL l, LL r) {
return (l + r) * (r - l + 1) / 2;
}
struct {
LL a[MAXN << 2], tag[MAXN << 2];
inline void pushup(int x) {
a[x] = a[x << 1] + a[x << 1 | 1];
}
inline void init() {
memset(tag, -1, sizeof(tag));
}
inline void pushdown(int x, int l, int r) {
if (tag[x] == -1) {
return;
}
int mid = (l + r) >> 1;
a[x << 1] = tag[x] * (mid - l + 1);
a[x << 1 | 1] = tag[x] * (r - mid);
tag[x << 1] = tag[x << 1 | 1] = tag[x];
tag[x] = -1;
}
void update(int rt, int l, int r, int ql, int qr, LL x) {
if (ql > qr) {
return;
}
if (ql <= l && r <= qr) {
a[rt] = x * (r - l + 1);
tag[rt] = x;
return;
}
pushdown(rt, l, r);
int mid = (l + r) >> 1;
if (ql <= mid) {
update(rt << 1, l, mid, ql, qr, x);
}
if (qr > mid) {
update(rt << 1 | 1, mid + 1, r, ql, qr, x);
}
pushup(rt);
}
LL query(int rt, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) {
return a[rt];
}
pushdown(rt, l, r);
int mid = (l + r) >> 1;
LL res = 0;
if (ql <= mid) {
res += query(rt << 1, l, mid, ql, qr);
}
if (qr > mid) {
res += query(rt << 1 | 1, mid + 1, r, ql, qr);
}
return res;
}
} t1;
struct {
LL a[MAXN << 2], tag[MAXN << 2];
inline void pushup(int x) {
a[x] = a[x << 1] + a[x << 1 | 1];
}
inline void init() {
memset(tag, -1, sizeof(tag));
}
inline void pushdown(int x, int l, int r) {
if (tag[x] == -1) {
return;
}
int mid = (l + r) >> 1;
a[x << 1] = tag[x] * calc(l, mid);
a[x << 1 | 1] = tag[x] * calc(mid + 1, r);
tag[x << 1] = tag[x << 1 | 1] = tag[x];
tag[x] = -1;
}
void update(int rt, int l, int r, int ql, int qr, LL x) {
if (ql > qr) {
return;
}
if (ql <= l && r <= qr) {
a[rt] = x * calc(l, r);
tag[rt] = x;
return;
}
pushdown(rt, l, r);
int mid = (l + r) >> 1;
if (ql <= mid) {
update(rt << 1, l, mid, ql, qr, x);
}
if (qr > mid) {
update(rt << 1 | 1, mid + 1, r, ql, qr, x);
}
pushup(rt);
}
LL query(int rt, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) {
return a[rt];
}
pushdown(rt, l, r);
int mid = (l + r) >> 1;
LL res = 0;
if (ql <= mid) {
res += query(rt << 1, l, mid, ql, qr);
}
if (qr > mid) {
res += query(rt << 1 | 1, mid + 1, r, ql, qr);
}
return res;
}
} t2;
set<pair<LL, LL>> S;
inline void upd(LL x, LL y, LL z) {
t1.update(1, 1, n, x + 1, y - 1, z * y);
t2.update(1, 1, n, x + 1, y - 1, -z);
}
void solve() {
t1.init();
t2.init();
cin >> n >> m >> q;
for (int i = 1; i <= m; ++i) {
cin >> a[i];
}
for (int i = 1; i <= m; ++i) {
cin >> b[i];
S.emplace(a[i], b[i]);
}
for (auto it = S.begin(); next(it) != S.end(); ++it) {
auto jt = next(it);
upd(it->first, jt->first, it->second);
}
while (q--) {
LL op, x, y;
cin >> op >> x >> y;
if (op == 1) {
S.emplace(x, y);
t1.update(1, 1, n, x, x, 0);
t2.update(1, 1, n, x, x, 0);
auto it = S.find(make_pair(x, y));
auto p = prev(it), q = next(it);
upd(p->first, it->first, p->second);
upd(it->first, q->first, it->second);
} else {
cout << t1.query(1, 1, n, x, y) + t2.query(1, 1, n, x, y) << endl;
}
}
}
int main() {
ios::sync_with_stdio(false);
solve();
return 0;
}
F.Fractal Origami(数学)(Div1-C)
题意:
有一张边长等于111个单位的正方形纸。在一次操作中,你将正方形的每个角折叠到纸的中心,从而形成边长等于12\dfrac{1}{\sqrt{2}}21个单位的另一个正方形。将这个正方形作为一个新的正方形,再次进行操作,一共重复NNN次。
完成这一系列运算后,打开纸张,看到上面有一些折痕线。每条折痕线都有两种类型:峰或谷。峰是指纸张向外折叠,谷是指纸张向内折叠。
计算纸张上所有山形折痕线的长度总和,将其称为MMM。同样,计算山谷折痕线的长度,并将其称为VVV。找出MV\dfrac{M}{V}VM的值。
可以证明这个值可以用A+B2A+B\sqrt{2}A+B2的形式表示,其中AAA和BBB是有理数。让BBB表示为不可约分数pq\dfrac{p}{q}qp,输出p×inv(q)p\times inv(q)p×inv(q)对999999893999999893999999893取模的结果。
其中inv(q)inv(q)inv(q)是qqq的模数转换
分析:
手动折叠,发现n=3n=3n=3时,M=2+22,V=2+42M=2+2\sqrt2,V=2+4\sqrt2M=2+22,V=2+42。于是猜测,第二次折叠开始,每次产生的山谷和山峰的长度相等。
考虑从第二次折叠开始,设当前纸的层数为kkk(事实上若当前是第iii次折叠,k=2i−1k=2^{i−1}k=2i−1)。则奇数层的纸展开后是山谷,偶数层的纸展开后是山峰。所以V=M+22V=M+2\sqrt2V=M+22恒成立。
这意味着我们只用计算nnn次折叠后的总折痕长度V+MV+MV+M,就能算出MMM和VVV的值。考虑每次折叠,纸的每一层的折痕长度为上一次折叠时×12\times \frac{1}{\sqrt2}×21,但是纸的层数为上一次折叠时×2\times2×2。所以每次折叠,总折痕长度为上一次的2\sqrt22倍。于是M=∑i=0n−222iM=\sum\limits_{i=0}^{n-2}2\sqrt{2}^iM=i=0∑n−222i,V=22+∑i=0n−222iV=2\sqrt2+\sum\limits_{i=0}^{n-2}2\sqrt{2}^iV=22+i=0∑n−222i。套用等比求和公式s=a(1−qn)1−qs=\frac{a(1-q^n)}{1-q}s=1−qa(1−qn)得出答案。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod = 999999893;
inline LL qpow(LL b, LL p) {
LL res = 1;
while (p) {
if (p & 1) {
res = res * b % mod;
}
b = b * b % mod;
p >>= 1;
}
return res;
}
LL n;
struct Node {
LL a, b;
Node(LL x = 0, LL y = 0) : a(x), b(y) {}
};
inline Node operator+(const Node &a, const Node &b) {
return Node((a.a + b.a) % mod, (a.b + b.b) % mod);
}
inline Node operator-(const Node &a, const Node &b) {
return Node((a.a - b.a + mod) % mod, (a.b - b.b + mod) % mod);
}
inline Node operator*(const Node &a, const Node &b) {
return Node((a.a * b.a + a.b * b.b * 2) % mod, (a.a * b.b + a.b * b.a) % mod);
}
inline Node operator/(const Node &x, const Node &y) {
LL a = x.a, b = x.b, c = y.a, d = y.b;
return Node((a * c - b * d * 2 % mod + mod) % mod * qpow((c * c - d * d * 2 % mod + mod) % mod, mod - 2) % mod,
(b * c - a * d % mod + mod) % mod * qpow((c * c - d * d * 2 % mod + mod) % mod, mod - 2) % mod);
}
inline Node qpow(Node b, LL p) {
Node res(1, 0);
while (p) {
if (p & 1) {
res = res * b;
}
b = b * b;
p >>= 1;
}
return res;
}
void solve() {
cin >> n;
Node a = Node(1, 0) - qpow(Node(0, 1), n - 1), b = Node(1, 0) - Node(0, 1);
Node x = a * Node(2, 0) / b;
Node y = x + Node(0, 2);
Node res = x / y;
cout << res.b << endl;
}
int main() {
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
学习交流
以下为学习交流QQ群,群号: 546235402,每周题解完成后都会转发到群中,大家可以加群一起交流做题思路,分享做题技巧,欢迎大家的加入。