10.2 总结
T1 躲避技能
赛时拿的是暴力的 \(40\) 分,没开 long。
40pts
用 LCA 乱搞,枚举每一个人去哪里,复杂度 \(\mathcal O(m! \log n)\) 。
AC
给每一个躲避点打上 \(-1\) 标记,当前点打上 \(1\) 标记,每一次向上转移边长乘子树标记和绝对值即可。
#include <string.h>
#include <algorithm>
#include <fstream>
#include <vector>
using namespace std;
class BigInt {
private:
static const int kMaxN = 201;
int Len = 0, Num[kMaxN] = {};
bool Neg = 0;
public:
BigInt() {
Len = 0, Neg = 0;
fill(Num, Num + kMaxN, 0);
}
BigInt(string WUYIXUAN_) {
if (WUYIXUAN_[0] == '-') {
WUYIXUAN_ = WUYIXUAN_.substr(1);
Neg = 1;
}
Len = WUYIXUAN_.size();
for (int i = 1, j = Len - 1; i <= Len; i++, j--) {
Num[i] = WUYIXUAN_[j] - '0';
}
}
BigInt(int WUYIXUAN_) {
string s = to_string(WUYIXUAN_);
if (s[0] == '-') {
s = s.substr(1);
Neg = 1;
}
Len = s.size();
for (int i = 1, j = Len - 1; i <= Len; i++, j--) {
Num[i] = s[j] - '0';
}
}
BigInt(long long WUYIXUAN_) {
string s = to_string(WUYIXUAN_);
if (s[0] == '-') {
s = s.substr(1);
Neg = 1;
}
Len = s.size();
for (int i = 1, j = Len - 1; i <= Len; i++, j--) {
Num[i] = s[j] - '0';
}
}
BigInt(const char *s) {
Len = strlen(s);
for (int i = 1, j = Len - 1; i <= Len; i++, j--) {
Num[i] = s[j] - '0';
}
}
int Size() {
while (!Num[Len]) {
Len--;
}
Len += !Len;
return Len;
}
BigInt SubInt(int start, int len) {
len = len == 0 ? Len - len + 1 : len;
BigInt ans;
int end = start + len;
ans.Len = end - start + 1;
for (int i = 1, j = start; i <= ans.Len; i++, j++) {
ans.Num[i] = this->Num[j];
}
end = ans.Size();
return ans;
}
bool CanSub(BigInt WUYIXUAN_, int pos) {
BigInt o = *this;
if (o.Len < pos + WUYIXUAN_.Len) {
return 0;
}
return this->SubInt(pos, WUYIXUAN_.Len) >= WUYIXUAN_;
}
friend BigInt operator+(BigInt WUYIXUAN_, BigInt WYX) {
if (WUYIXUAN_.Neg && !WYX.Neg) {
WUYIXUAN_.Neg = 0;
return WYX - WUYIXUAN_;
} else if (!WUYIXUAN_.Neg && WYX.Neg) {
WYX.Neg = 0;
return WUYIXUAN_ - WYX;
}
BigInt ans;
ans.Len = max(WUYIXUAN_.Len, WYX.Len);
for (int i = 1; i <= ans.Len; i++) {
ans.Num[i] = WUYIXUAN_.Num[i] + WYX.Num[i];
}
for (int i = 1; i <= ans.Len; i++) {
ans.Num[i + 1] += ans.Num[i] / 10, ans.Num[i] %= 10;
}
if (ans.Num[ans.Len + 1]) {
ans.Len++;
}
return ans;
}
BigInt operator+=(BigInt WUYIXUAN_) {
*this = *this + WUYIXUAN_;
return *this;
}
friend bool operator<(BigInt WUYIXUAN_, BigInt WYX) {
if (WUYIXUAN_.Neg ^ WYX.Neg) {
return WUYIXUAN_.Neg != 1;
}
if (WUYIXUAN_.Len != WYX.Len) {
return WUYIXUAN_.Len < WYX.Len;
}
for (int i = 1; i <= WUYIXUAN_.Len; i++) {
if (WUYIXUAN_.Num[i] != WYX.Num[i]) {
return WUYIXUAN_.Num[i] < WYX.Num[i];
}
}
return 0;
}
friend bool operator==(BigInt WUYIXUAN_, BigInt WYX) {
if (WUYIXUAN_.Len != WYX.Len) {
return 0;
}
for (int i = 1; i <= WUYIXUAN_.Len; i++) {
if (WUYIXUAN_.Num[i] != WYX.Num[i]) {
return 0;
}
}
return 1;
}
friend bool operator>(BigInt WUYIXUAN_, BigInt WYX) {
return !(WUYIXUAN_ < WYX);
}
friend bool operator<=(BigInt WUYIXUAN_, BigInt WYX) {
return WUYIXUAN_ < WYX || WUYIXUAN_ == WYX;
}
friend bool operator>=(BigInt WUYIXUAN_, BigInt WYX) {
return WUYIXUAN_ > WYX || WUYIXUAN_ == WYX;
}
friend ostream &operator<<(ostream &cout, BigInt num) {
if (num.Neg) {
cout.put('-');
}
for (int i = num.Len; i >= 1; i--) {
cout.put(num.Num[i] + '0');
}
return cout;
}
friend BigInt operator-(BigInt WUYIXUAN_, BigInt WYX) {
if (WUYIXUAN_.Neg && !WYX.Neg) {
WUYIXUAN_.Neg = 0;
BigInt ret = WUYIXUAN_ + WYX;
ret.Neg = 1;
return ret;
}
if (WYX.Neg) {
WYX.Neg = 0;
return WUYIXUAN_ + WYX;
} else if (WUYIXUAN_.Neg) {
WUYIXUAN_.Neg = 0;
return WYX - WUYIXUAN_;
}
if (WUYIXUAN_ < WYX) {
BigInt ret = WYX - WUYIXUAN_;
ret.Neg = 1;
return ret;
}
BigInt ans;
ans.Len = WUYIXUAN_.Len;
for (int i = 1; i <= ans.Len; i++) {
ans.Num[i] = WUYIXUAN_.Num[i] - WYX.Num[i];
}
for (int i = 1; i <= ans.Len; i++) {
if (ans.Num[i] < 0) {
ans.Num[i + 1]--, ans.Num[i] += 10;
}
}
while (ans.Len && !ans.Num[ans.Len]) {
ans.Len--;
}
if (!ans.Len) {
ans.Len++;
}
return ans;
}
BigInt operator-=(BigInt WUYIXUAN_) {
*this = *this - WUYIXUAN_;
return *this;
}
friend BigInt operator*(BigInt WUYIXUAN_, BigInt WYX) {
BigInt ans;
ans.Len = WUYIXUAN_.Len + WYX.Len;
for (int i = 1; i <= WUYIXUAN_.Len; i++) {
for (int j = 1; j <= WYX.Len; j++) {
ans.Num[i + j - 1] += WUYIXUAN_.Num[i] * WYX.Num[j];
}
}
for (int i = 1; i <= ans.Len; i++) {
ans.Num[i + 1] += ans.Num[i] / 10, ans.Num[i] %= 10;
}
while (!ans.Num[ans.Len]) {
ans.Len--;
}
ans.Len += !ans.Len;
ans.Neg = WUYIXUAN_.Neg ^ WYX.Neg;
return ans;
}
BigInt operator*=(BigInt WUYIXUAN_) {
*this = *this * WUYIXUAN_;
return *this;
}
BigInt operator<<(int WUYIXUAN_) {
BigInt o = *this;
for (int i = 0; i < WUYIXUAN_; i++) {
o *= 2;
}
return o;
}
BigInt operator<<=(int WUYIXUAN_) {
*this = *this << WUYIXUAN_;
return *this;
}
BigInt &operator++() {
*this += 1;
return *this;
}
BigInt operator++(int) {
BigInt temp = *this;
*this += 1;
return temp;
}
BigInt &operator--() {
*this -= 1;
return *this;
}
BigInt operator--(int) {
BigInt temp = *this;
*this -= 1;
return temp;
}
friend istream &operator>>(istream &cin, BigInt &num) {
char c = cin.get();
while (!isdigit(c) && c != '-') {
c = cin.get();
}
if (isdigit(c)) {
num = c - '0';
} else {
num.Neg = 1;
}
c = cin.get();
while (isdigit(c)) {
num = num * 10 + (c - '0');
c = cin.get();
}
return cin;
}
};
using pa = pair<int, BigInt>;
const int kMaxN = 1e6 + 1;
ifstream cin("skill.in");
ofstream cout("skill.out");
int n, m, sz[kMaxN];
vector<pa> g[kMaxN];
BigInt ans;
void Dfs(int x, int fa) {
for (auto i : g[x]) {
if (i.first != fa) {
Dfs(i.first, x);
sz[x] += sz[i.first];
ans += i.second * abs(sz[i.first]);
}
}
}
int main() {
cin >> n >> m;
for (int i = 1, x; i <= m; i++) {
cin >> x, sz[x]++;
}
for (int i = 1, x; i <= m; i++) {
cin >> x, sz[x]--;
}
for (int i = 1, u, v, x; i < n; i++) {
string w;
cin >> u >> v >> w;
reverse(w.begin(), w.end());
g[u].push_back({v, BigInt(w)});
g[v].push_back({u, BigInt(w)});
}
Dfs(1, 0);
cout << ans << '\n';
return 0;
}
T2 奶茶兑换券
暴力不会。
AC
首先把所有 \(b_i\) 大于 \(m\) 的处理了,然后给每一个 \(b_i \bmod m\),然后可以对剩下进行排序然后双指针 \(b_i \ge \lfloor \dfrac{d}{2} \rfloor\) 和 \(b_i \le \lfloor \dfrac{d}{2} \rfloor\) 的两个部分。
#include <algorithm>
#include <fstream>
using namespace std;
using ll = long long;
const int kMaxN = 1e5 + 5;
ifstream cin("tea.in");
ofstream cout("tea.out");
struct Node {
int a, b;
} p[kMaxN];
int main() {
int n, m;
cin >> n >> m;
ll ans = 0;
for (int i = 1; i <= n; i++) {
cin >> p[i].a >> p[i].b;
p[i].b %= m;
if (!p[i].b) {
--i, --n;
continue;
}
ans += (ll)p[i].a * (m - p[i].b);
}
sort(p + 1, p + n + 1, [](Node a, Node b) { return a.b < b.b; });
int i = 1, j = n;
while (i < j) {
if (p[i].b + p[j].b > m) {
--j;
continue;
}
int t = min(p[i].a, p[j].a);
p[i].a -= t, p[j].a -= t, ans -= (ll)t * m;
if (!p[i].a) {
i++;
}
if (!p[j].a) {
--j;
}
}
if (p[i].b * 2 <= m) {
ans -= (ll)p[i].a / 2 * m;
}
cout << ans << '\n';
return 0;
}
T3 帮助
40 pts
枚举每两个同学,看看能不能互帮互助,复杂度 \(\mathcal O(n^2)\) 。
AC
先把 \(t_i,a_i, b_i, c_i, d_i\) 全部离散化,然后排序,预处理出每个人能帮助的区间和给予帮助的区间(因为排了序是一段连续的区间),用差分+树状数组维护每个人的贡献。
#include <algorithm>
#include <fstream>
#include <vector>
using namespace std;
using ll = long long;
const int kMaxN = 1e5 + 5;
ifstream cin("help.in");
ofstream cout("help.out");
int f[kMaxN], t[kMaxN], num[kMaxN], c[kMaxN], d[kMaxN];
ll ans[kMaxN], sum[kMaxN];
vector<int> h[kMaxN], g[kMaxN];
int len;
int lowbit(int x) {
return x & -x;
}
void Add(int x, int val) {
while (x <= len + 1) {
sum[x] += val, x += lowbit(x);
}
}
ll Modify(int x) {
ll ret = 0;
while (x > 0) {
ret += sum[x], x -= lowbit(x);
}
return ret;
}
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> f[i];
}
for (int i = 1; i <= n; ++i) {
cin >> t[i], num[i] = t[i];
}
sort(num + 1, num + n + 1);
len = unique(num + 1, num + n + 1) - num - 1;
for (int i = 1; i <= n; i++) {
int a, b;
t[i] = lower_bound(num + 1, num + len + 1, t[i]) - num;
h[t[i]].push_back(i);
cin >> a >> b;
a = lower_bound(num + 1, num + len + 1, a) - num;
b = upper_bound(num + 1, num + len + 1, b) - num - 1;
if (a <= b) {
g[b].push_back(i);
g[a - 1].push_back(-i);
}
cin >> c[i] >> d[i];
c[i] = lower_bound(num + 1, num + len + 1, c[i]) - num;
d[i] = upper_bound(num + 1, num + len + 1, d[i]) - num - 1;
if (!(a <= t[i] && t[i] <= b && c[i] <= t[i] && t[i] <= d[i])) {
ans[i] = f[i];
}
}
for (int i = 1; i <= len; ++i) {
for (auto j : h[i]) {
if (c[j] <= d[j]) {
Add(c[j], f[j]);
Add(d[j] + 1, -f[j]);
}
}
for (auto j : g[i]) {
int flag = (j < 0) ? -1 : 1;
j = abs(j);
ans[j] += Modify(t[j]) * flag;
}
}
for (int i = 1; i <= n; ++i) {
cout << ans[i] << ' ';
}
return 0;
}
T4 神奇的变换
Subtask 1
暴力即可
Subtask 2
不会。
Subtack 3
先算出每一个 \(a_i\) 的每一个约数的个数,然后询问时用前缀和算出来即可,复杂度 \(\mathcal O(1000(n+q))\)。
Subtask 4
用前缀积然后计算即可。

浙公网安备 33010602011771号