无锡学院第一届程序设计竞赛&GPLT选拔赛
链接: https://ac.nowcoder.com/acm/contest/77760
\(\\\)
A 美丽字符串
从左起遍历,记长度为 \(i\) 的子串的出现次数为 \(S[i]\),那么任意长度为 \(j\) 的连续子串,其贡献是 \(1\),对于所有的 \(S[k],k∈[1,j]\) 。容易想到用线段树来统计贡献。复杂度 \(O(n logn)\)。
点击查看代码
#pragma GCC optimize("unroll-loops, Ofast")
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
#define endl '\n'
#define lowbit(x) x & -x
constexpr i64 Mod = 1000000007;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
i64 n, m, k;
void init() {}
class Z {
using i64 = long long;
static const i64 Mod = 1000000007;
public:
i64 num;
Z() = default;
Z(i64 _num) : num((_num % Mod + Mod) % Mod) {}
i64 val() const {
return num;
}
Z &operator = (i64 b) {
return *this = Z(b);
}
friend bool operator < (Z a, Z b) {
return a.num < b.num;
}
friend bool operator >(Z a, Z b) {
return a.num > b.num;
}
friend bool operator <=(Z a, Z b) {
return a.num <= b.num;
}
friend bool operator>=(Z a, Z b) {
return a.num >= b.num;
}
friend bool operator==(Z a, Z b) {
return a.num == b.num;
}
friend Z operator + (Z a, Z b) {
return Z((a.num + b.num) % Mod);
}
friend Z &operator += (Z &a,Z b) {
return a = a + b;
}
friend Z operator + (Z a, i64 b) {
return a + Z(b);
}
friend Z &operator += (Z &a, i64 b) {
return a = a + b;
}
friend Z &operator ++ (Z &a) {
return a += 1;
}
friend Z operator ++ (Z &a, int) {
Z copy(a);
a += 1;
return copy;
}
friend Z operator - (Z a, Z b) {
return Z(((a.num - b.num) % Mod + Mod) % Mod);
}
friend Z &operator -= (Z &a, Z b) {
return a = a - b;
}
friend Z operator - (Z a, i64 b) {
return a - Z(b);
}
friend Z &operator -= (Z &a, i64 b) {
return a = a - b;
}
friend Z &operator -- (Z &a) {
return a -= 1;
}
friend Z operator -- (Z &a, int) {
Z copy(a);
a -= 1;
return copy;
}
friend Z operator * (Z a, Z b) {
return Z((long long)a.num * b.num % Mod);
}
friend Z &operator *= (Z &a, Z b) {
return a = a * b;
}
friend Z operator * (Z a, i64 b) {
return a * Z(b);
}
friend Z &operator *= (Z &a, i64 b) {
return a = a * b;
}
Z inv() {
i64 ans = 1;
i64 a = num;
i64 b = Mod - 2;
while (b) {
if (b & 1) ans = ans * a % Mod;
a = a * a % Mod;
b >>= 1;
}
return Z(ans);
}
friend Z operator / (Z a, Z b) {
return a * b.inv();
}
friend Z &operator /= (Z &a, Z b) {
return a = a / b;
}
friend Z operator / (Z a, i64 b) {
return a / Z(b);
}
friend Z &operator /= (Z &a, i64 b) {
return a = a / b;
}
friend std::istream &operator>>(std::istream &is, Z &a) {
int v;
is >> v;
a = Z(v);
return is;
}
friend std::ostream &operator<<(std::ostream &os, const Z &a) {
return os << a.val();
}
};
template<class Info, class Tag>
struct LazySegmentTree {
const int n;
std::vector<Info> info;
std::vector<Tag> tag;
LazySegmentTree(int n) : n(n), info(4 << std::__lg(n)), tag(4 << std::__lg(n)) {}
LazySegmentTree(std::vector<Info> init) : LazySegmentTree(init.size()) {
std::function<void(int, int, int)> build = [&](int p, int l, int r) {
if (r - l == 1) {
info[p] = init[l];
return;
}
int m = (l + r) / 2;
build(2 * p, l, m);
build(2 * p + 1, m, r);
pull(p);
};
build(1, 0, n);
}
void pull(int p) {
info[p] = info[2 * p] + info[2 * p + 1];
}
void apply(int p, const Tag &v) {
info[p].apply(v);
tag[p].apply(v);
}
void push(int p) {
apply(2 * p, tag[p]);
apply(2 * p + 1, tag[p]);
tag[p] = Tag();
}
void modify(int p, int l, int r, int x, const Info &v) {
if (r - l == 1) {
info[p] = v;
return;
}
int m = (l + r) / 2;
push(p);
if (x < m) {
modify(2 * p, l, m, x, v);
} else {
modify(2 * p + 1, m, r, x, v);
}
pull(p);
}
void modify(int p, const Info &v) {
modify(1, 0, n, p, v);
}
Info rangeQuery(int p, int l, int r, int x, int y) {
if (l >= y || r <= x) {
return Info();
}
if (l >= x && r <= y) {
return info[p];
}
int m = (l + r) / 2;
push(p);
return rangeQuery(2 * p, l, m, x, y) + rangeQuery(2 * p + 1, m, r, x, y);
}
Info rangeQuery(int l, int r) {
return rangeQuery(1, 0, n, l, r);
}
void rangeApply(int p, int l, int r, int x, int y, const Tag &v) {
if (l >= y || r <= x) {
return;
}
if (l >= x && r <= y) {
apply(p, v);
return;
}
int m = (l + r) / 2;
push(p);
rangeApply(2 * p, l, m, x, y, v);
rangeApply(2 * p + 1, m, r, x, y, v);
pull(p);
}
void rangeApply(int l, int r, const Tag &v) {
return rangeApply(1, 0, n, l, r, v);
}
void half(int p, int l, int r) {
if (info[p].act == 0) {
return;
}
if ((info[p].min + 1) / 2 == (info[p].max + 1) / 2) {
apply(p, {-(info[p].min + 1) / 2});
return;
}
int m = (l + r) / 2;
push(p);
half(2 * p, l, m);
half(2 * p + 1, m, r);
pull(p);
}
void half() {
half(1, 0, n);
}
};
struct Tag {
i64 add = 0;
void apply(Tag t) {
add += t.add;
}
};
struct Info {
i64 Act = 1;
i64 Sum = 0;
void apply(Tag t) {
Sum += Act * t.add;
}
};
Info operator+(Info a, Info b) {
Info c;
c.Sum = a.Sum + b.Sum;
c.Act = a.Act + b.Act;
return c;
}
i64 power(i64 a, i64 b) {
const i64 Mod = 1000000007;
i64 res = 1;
i64 base = a;
while (b) {
if (b & 1) {
res = res * base % Mod;
}
base = base * base % Mod;
b >>= 1;
}
return res;
}
void solve() {
cin >> n;
string s;
cin >> s;
LazySegmentTree<Info, Tag> seg(n + 5);
for (int i = 0; i < s.size(); i++) {
if (i == s.size() - 1) {
seg.rangeApply(1, 2, Tag{1});
break;
}
for (int j = i; j < s.size(); j++) {
if (j == i || s[j] == s[j - 1] + 1) {
seg.rangeApply(1, j - i + 2, Tag{1});
if (j == s.size() - 1) {
i = j;
break;
}
} else {
i = j - 1;
break;
}
}
}
Z ans = 0;
for (int i = 1; i <= n; i++) {
i64 pw = seg.rangeQuery(i, i + 1).Sum;
ans += pw * power(2, i);
}
cout << ans << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
init();
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
\(\\\)
B 帕鲁对对碰
每次伤害为 \(2^i\),最少的攻击次数显然为血量二进制中 \(1\) 的个数,可以用 __\(builtin\)_$ \ popcount()$ 方法计算。复杂度小于 \(O(logn)\) 。
点击查看代码
#pragma GCC optimize("unroll-loops, Ofast")
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
#define endl '\n'
#define lowbit(x) x & -x
constexpr i64 Mod = 1000000007;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
i64 n, m, k;
void init() {}
void solve() {
cin >> n >> m;
int x = __builtin_popcount(n);
int y = __builtin_popcount(m);
cout << (y <= x ? "Alice" : "Bob") << " " << min(x, y) << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
init();
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
\(\\\)
C 植树青年
考虑二分答案,对于当前的天数 \(x\), 需要的骨灰数量为 \(\displaystyle\sum_{i=1}^n max(a[i] - x, 0)\),这一部分可以 \(O(n)\) 计算,复杂度 \(O(n logn)\)。
点击查看代码
#pragma GCC optimize("unroll-loops, Ofast")
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
#define endl '\n'
#define lowbit(x) x & -x
constexpr i64 Mod = 1000000007;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
i64 n, m, k;
void init() {}
void solve() {
cin >> n >> m;
vector<i64> p(n);
for (int i = 0; i < n; i++) {
cin >> p[i];
}
auto check= [&](i64 u) -> bool {
i64 cur = 0;
for (auto c: p) {
if (c > u) {
cur += c - u;
}
}
return cur <= m;
};
i64 l = 0, r = 1e9;
while (l <= r) {
i64 mid = l + r >> 1;
if (check(mid)) {
r = mid - 1;
} else {
l = mid + 1;
}
}
cout << l << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
init();
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
\(\\\)
D 单词典
暴力枚举即可。复杂度 \(O(nm)\),\(n,m\) 为字符串的长度。
点击查看代码
#pragma GCC optimize("unroll-loops, Ofast")
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
#define endl '\n'
#define lowbit(x) x & -x
constexpr i64 Mod = 1000000007;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
i64 n, m, k;
void init() {}
void solve() {
string a, b;
cin >> a >> b;
int ans = 0;
for (int i = 0; i < a.size(); i++) {
ans += a.substr(i, b.size()) == b;
}
cout << ans << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
init();
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
\(\\\)
E 胡萝卜(easy)
给定的所有胡萝卜重量都大于 \(0\),即每次捡胡萝卜对答案的贡献都是正的,所以全部选择即可。最大和最小的胡萝卜可以用 \(multiset\) 维护,复杂度 \(O(n logn)\)。
点击查看代码
#pragma GCC optimize("unroll-loops, Ofast")
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
#define endl '\n'
#define lowbit(x) x & -x
constexpr i64 Mod = 1000000007;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
i64 n, m, k;
void init() {}
void solve() {
cin >> n >> m;
vector<i64> p(n + 1);
for (int i = 1; i <= n; i++) {
cin >> p[i];
}
vector<i64> q(n + 1);
while (m--) {
cin >> k;
q[abs(k)] = k;
}
multiset<i64> st;
for (int i = 1; i <= n; i++) {
if (q[i] > 0 && st.size()) {
st.extract(*st.rbegin());
}
if (q[i] < 0 && st.size()) {
st.extract(*st.begin());
}
st.insert(p[i]);
}
i64 ans = 0;
for (auto c: st) {
ans += c;
}
cout << ans << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
init();
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
\(\\\)
F MHWI
统计四条线段和给定的所有线段的相交情况,给定线段的单个贡献至多为 \(1\),考虑用矢量叉积计算,若两线段相交,当且仅当下面条件至少存在一个,
\(1.\) 线段都跨越了包含另一条线段的直线。
\(2.\) 一条线段的一个端点落在另一条线段上。
注意标记单个线段的贡献情况。复杂度 \(O(n)\)。
点击查看代码
#pragma GCC optimize("unroll-loops, Ofast")
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
#define endl '\n'
#define lowbit(x) x & -x
constexpr i64 Mod = 1000000007;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
i64 n, m, k;
void init() {}
int sgn(i64 x) { return x < 0 ? -1 : x > 0; }
struct Point {
i64 x, y;
Point(i64 x = 0, i64 y = 0) : x(x), y(y) {}
Point operator- (const Point &B) const { return Point(x - B.x, y - B.y); }
Point operator+ (const Point &B) const { return Point(x + B.x, y + B.y); }
i64 operator^ (const Point &B) const { return x * B.y - y * B.x; }
i64 operator* (const Point &B) const { return x * B.x + y * B.y; }
};
int Cross(Point a, Point b, Point c) { return sgn((b - a) ^ (c - a)); }
bool OnSegment(Point P, Point A, Point B) {
Point PA = A - P, PB = B - P;
return sgn(PA ^ PB) == 0 && sgn(PA * PB) <= 0;
}
bool Intersect(Point a, Point b, Point c, Point d) {
if (OnSegment(a, c, d) || OnSegment(b, c, d) || OnSegment(c, a, b) || OnSegment(d, a, b)) return 1;
if (Cross(a, b, c) * Cross(a, b, d) >= 0) return 0;
if (Cross(c, d, a) * Cross(c, d, b) >= 0) return 0;
return 1;
}
void solve() {
i64 d;
cin >> n >> d;
int ans = 0;
vector<bool> vis(n);
vector<pair<Point, Point>> p(n);
for (int i = 0; i < n; i++) {
cin >> p[i].first.x >> p[i].first.y >> p[i].second.x >> p[i].second.y;
}
Point O(0, 0), A(0, d), B(0, -d), C(d, 0), D(-d, 0);
for (int i = 0; i < n; i++) {
auto check = Intersect(O, A, p[i].first, p[i].second);
if (check && !vis[i]) {
ans += check;
vis[i] = true;
}
}
for (int i = 0; i < n; i++) {
auto check = Intersect(O, B, p[i].first, p[i].second);
if (check && !vis[i]) {
ans += check;
vis[i] = true;
}
}
for (int i = 0; i < n; i++) {
auto check = Intersect(O, C, p[i].first, p[i].second);
if (check && !vis[i]) {
ans += check;
vis[i] = true;
}
}
for (int i = 0; i < n; i++) {
auto check = Intersect(O, D, p[i].first, p[i].second);
if (check && !vis[i]) {
ans += check;
vis[i] = true;
}
}
if (!ans) {
cout << "No" << endl;
return ;
}
cout << "Yes" << endl;
cout << ans << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
init();
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
\(\\\)
G 排列再现
贪心,升序排序之后按 \(a[i] = i\) 计算贡献。一种简易猜想方法是考虑序列 \({\{a[1],a[2],……,a[n]\}}\) 为非降序序列,此时的答案为 \(\sum_{i=1}^n \mid a[i]-i \ \mid\),任意交换一对元素 \(( \ a[x], \ a[y], \ 0 \leqslant x<y \leqslant 1)\),相比于非降序序列所减小的计算量为 \(I=( \mid a[x]-x\mid + \mid a[y]-y \mid) - ( \mid a[x]-y\mid + \mid a[y]-x \mid)\),考虑证明 \(I<0\),移项后平方,即证明\((\mid a[x]-x \mid + \mid a[y]-y \mid)^2 < (\mid a[x]-y\mid + \mid a[y] - x \mid)^2\),根据 \(wolfram \ alpha\),它是对的。
点击查看代码
#pragma GCC optimize("unroll-loops, Ofast")
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
#define endl '\n'
#define lowbit(x) x & -x
constexpr i64 Mod = 1000000007;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
i64 n, m, k;
void init() {}
void solve() {
cin >> n;
vector<i64> p(n + 1);
for (int i = 1; i <= n; i++) {
cin >> p[i];
}
sort(p.begin() + 1, p.end());
i64 ans = 0;
for (int i = 1; i <= n; i++) {
ans += abs(i - p[i]);
}
cout << ans << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
init();
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
\(\\\)
H 如听仙乐耳暂明
对每首歌的时长取模后用 \(map\) 记录数量,逐一求解即可。复杂度\(O(max(nlogn,30logn))\)。
点击查看代码
#pragma GCC optimize("unroll-loops, Ofast")
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
#define endl '\n'
#define lowbit(x) x & -x
constexpr i64 Mod = 1000000007;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
i64 n, m, k;
void init() {}
void solve() {
cin >> n;
vector<i64> p(n);
map<i64, i64> mp;
for (int i = 0; i < n; i++) {
cin >> p[i];
p[i] %= 60;
mp[p[i]]++;
}
int ans = 0;
for (auto &[x, y]: mp) {
if (x > 30) {
break;
}
if (x == 30) {
ans += y / 2 * 2;
} else if (x) {
if (mp.find(60 - x) != mp.end()) {
int u = min(y, mp[60 - x]);
ans += 2 * u;
}
} else {
ans += y;
}
}
cout << ans << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
init();
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
\(\\\)
I 胡萝卜(hard)
和 easy 版本的做法一样,在最后计算答案时,去掉所有负数重量的胡萝卜即可。复杂度 \(O(nlogn)\)。
点击查看代码
#pragma GCC optimize("unroll-loops, Ofast")
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
#define endl '\n'
#define lowbit(x) x & -x
constexpr i64 Mod = 1000000007;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
i64 n, m, k;
void init() {}
void solve() {
cin >> n >> m;
vector<i64> p(n + 1);
for (int i = 1; i <= n; i++) {
cin >> p[i];
}
vector<i64> q(n + 1);
while (m--) {
cin >> k;
q[abs(k)] = k;
}
multiset<i64> st;
for (int i = 1; i <= n; i++) {
if (q[i] > 0 && st.size()) {
st.extract(*st.rbegin());
}
if (q[i] < 0 && st.size()) {
st.extract(*st.begin());
}
st.insert(p[i]);
}
i64 ans = 0;
for (auto c: st) {
if (c < 0) {
continue;
}
ans += c;
}
cout << ans << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
init();
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
\(\\\)
J 计算机网络
对于两点之间是否连通,可以用并查集维护,或者不做也可以。对于答案的求解,考虑 \(BFS\) ,用 \(map\) 记录每个点的状态做剪枝。复杂度不会算,大约是 \(O(nlog^2n)\) 量级的。
点击查看代码
#pragma GCC optimize("unroll-loops, Ofast")
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
#define endl '\n'
#define lowbit(x) x & -x
constexpr i64 Mod = 1000000007;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
i64 n, m, k;
void init() {}
struct g {
i64 v;
i64 u;
g(i64 u_, i64 v_): v(v_), u(u_) {}
};
struct DSU {
using i64 = long long;
vector<i64> fa, siz;
DSU(i64 n) : fa(n + 1), siz(n + 1, 1) { iota(fa.begin(), fa.end(), 0); };
i64 find(i64 x) {
while (x != fa[x]) x = fa[x] = fa[fa[x]];
return x;
}
i64 size(i64 x) { return siz[find(x)]; }
i64 len() {
i64 ans = 0;
for (int i = 1; i <= n; i++) {
if(find(i) == i) ans++;
}
return ans;
}
bool same(i64 x, i64 y) { return find(x) == find(y); }
bool merge(i64 x, i64 y) {
x = find(x), y = find(y);
if (x == y) return false;
siz[y] += siz[x];
fa[x] = y;
return true;
}
};
constexpr int N = 1e5 + 5;
void solve() {
int s, t;
cin >> n >> m >> s >> t;
DSU dsu(N);
vector<i64> vis(n + 1, 1e18);
map<pair<int, int>, i64> dis;
vector<vector<int>> adj(n + 1);
vector<i64> p(n + 1);
for (int i = 1; i <= n; i++) {
cin >> p[i];
}
i64 x, y, z;
while (m--) {
cin >> x >> y >> z;
dis[{x, y}] = dis[{y, x}] = z;
adj[x].emplace_back(y);
adj[y].emplace_back(x);
dsu.merge(x, y);
}
if (!dsu.same(s, t)) {
cout << -1 << endl;
return ;
}
i64 ans = 1e18;
auto bfs = [&]() -> void {
queue<g> q;
g st(s, 0);
q.push(st);
vis[s] = 0;
while (!q.empty()) {
auto c = q.front();
q.pop();
if (c.u == t) {
ans = min(ans, c.v);
}
for (auto d: adj[c.u]) {
g nxt(d, c.v + p[c.u] + dis[{c.u, d}]);
if (nxt.v < vis[d]) {
q.push(nxt);
vis[d] = nxt.v;
}
}
}
};
bfs();
cout << ans << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
init();
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
\(\\\)
K Boom
传统扫描线的做法会出现精度问题,观察此题 \(n\) 的范围非常小,用 \(DFS\) 维护当前点集,直接打个裸的暴力即可。复杂度 \(O(n^2 \cdot 2^n)\)。
点击查看代码
#pragma GCC optimize("unroll-loops, Ofast")
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
#define endl '\n'
#define lowbit(x) x & -x
constexpr i64 Mod = 1000000007;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
i64 n, m, k;
void init() {}
constexpr double eps = 1e-9;
constexpr double PI = acos(-1.0);
int sgn(double x) { return x < -eps ? -1 : x > eps; }
struct Point {
i64 x, y;
Point(i64 x = 0, i64 y = 0) : x(x), y(y) {}
};
double dist(Point a, Point b) {
return ((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
vector<Point> p;
vector<Point> q;
int ans = 0;
i64 R;
bool check() {
if (p.size() <= 1) {
return true;
}
for (int i = 0; i < p.size(); i++) {
for (int j = 0; j < p.size(); j++) {
if (dist(p[i], p[j]) > R * R * 4) {
return false;
}
}
}
return true;
}
void dfs(int u) {
if (u && check()) {
ans = max(ans, int(p.size()));
}
if (u == n) {
return ;
}
p.emplace_back(q[u]);
dfs(u + 1);
p.pop_back();
dfs(u + 1);
}
void solve() {
cin >> n >> R;
q.resize(n);
for (int i = 0; i < n; i++) {
cin >> q[i].x >> q[i].y;
}
dfs(0);
assert(ans > 0);
cout << ans << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
init();
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
\(\\\)
L 分衣服
按照编号顺序模拟,考虑用树状数组维护每个点处是否有衣服。当拿走了当前衣服以后,该点置 \(0\) ,那么对于位置为 \(i\) 处的衣服,其贡献为 \(RangeQuery(1, i-1)\),暴力枚举同编号的每件衣服的位置即可。复杂度 \(O(nlogn)\)。
点击查看代码
#pragma GCC optimize("unroll-loops, Ofast")
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
#define endl '\n'
#define lowbit(x) x & -x
constexpr i64 Mod = 1000000007;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
i64 n, m, k;
void init() {}
template<typename T>
struct Fenwick {
using i64 = long long;
const i64 n;
std::vector<T> tree;
Fenwick(i64 n) : n(n), tree(n + 1) {};
Fenwick(int n) : n(n), tree(n + 1) {};
Fenwick(double n): n(i64(n)), tree(i64(n) + 1) {};
T Query(i64 x) {
T res = 0;
for (int i = x; i > 0; i -= (i & -i)) {
res += tree[i];
}
return res;
}
void upd(i64 l, T z) {
if (l <= 0) return;
for (int i = l; i <= n; i += (i & -i)) {
tree[i] += z;
}
}
T rangeQuery(i64 l, i64 r) {
if (l > r) {
return 0;
}
return Query(std::min(n, r)) - Query(std::max(0LL, l - 1));
}
};
constexpr int N = 1e5 + 5;
void solve() {
cin >> n >> m;
Fenwick<i64> fen(N);
vector<vector<int>> pos(n + 1);
vector<int> p(m + 1);
for (int i = 1; i <= m; i++) {
cin >> p[i];
fen.upd(i, 1);
pos[p[i]].emplace_back(i);
}
i64 ans = 0;
for (int i = 1; i <= n; i++) {
for (auto c: pos[i]) {
ans += fen.rangeQuery(1, c - 1);
fen.upd(c, -1);
}
}
cout << ans << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
init();
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
浙公网安备 33010602011771号