河南萌新聯賽2024第(四)場:河南理工大學
A-該出奇兵了_河南萌新聯賽2024第(四)場:河南理工大學 (nowcoder.com)
思路
一次奇襲相當於割掉了一個點,而每割掉一個點可能會產生一個或多個連通分量,所以我們需要計算刪掉一個節點後其新產生的聯通分量的貢獻。
以割點為分界點,分為兩棵子樹,在縮點過程中將刪掉割點後產生的 \(son_x-1(son\text{表示x的兒子個數})\) 個子樹的貢獻先記錄到 \(ans_u\) 裡,然後 \(sum_u\) 記該子樹的大小,最後再加上 \(a_u\) 表示從該割點起表示的一棵子樹的大小,到時候計算剩下一棵子樹直接用該聯通塊的大小減去 \(sum_i\) 即可。
記 \(rt_i\) 表示點 \(i\) 所在的聯通塊編號,\(S\) 表示某一聯通塊的貢獻,\(N\) 表示聯通塊的個數,將 \(i\) 點割掉後得到了 \(m\) 個新聯通分量,則對於刪掉 \(i\) 點的答案為:
\[\sum\limits_{tr_i=1}^NS_{tr_i}^2-S_{tr_i}^2+\sum\limits_{j=1}^mS_j
\]
程式碼
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using i128 = __int128;
istream &operator>>(istream &is, i128 &x) {
string s;
is >> s;
bool neg = false;
x = 0;
for (char c : s) {
if (c == '-') neg = true;
else x = x * 10 + (c - '0');
}
if (neg) x = -x;
return is;
}
ostream &operator<<(ostream &os, i128 x) {
if (x == 0) os << 0;
else {
string s, t;
if (x < 0) x = -x, t = "-";
while (x) s.push_back('0' + x % 10), x /= 10;
reverse(s.begin(), s.end());
os << t << s;
}
return os;
}
constexpr int N = 3e5 + 10;
vector<i128> ans(N), sz(N), a(N), sum(N);
struct SCC {
int top = 0, cntscc = 0, dfncnt = 0, n;
vector<int> dfn, low, stk, instk;
vector<int> sccnum, sccid;
vector<vector<int>> g, scc;
SCC(int n_): n(n_) {
//縮點
dfn.assign(n + 1, 0);
low.assign(n + 1, 0);
stk.assign(n + 1, 0);
sccnum.assign(n + 1, 0);
sccid.assign(n + 1, 0);
instk.assign(n + 1, 0);
g.resize(n + 1);
scc.resize(n + 1);
}
void add(int u, int v) {
g[u].push_back(v);
}
//縮點
void tarjan(int u) {
dfn[u] = low[u] = ++dfncnt;
stk[++top] = u;
instk[u] = 1;
i128 s = 0;
for (auto v : g[u]) {
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
sz[u] += sz[v];
if (dfn[u] <= low[v]) {
ans[u] += sz[v] * sz[v];
s += sz[v];
}
} else if (instk[v]) {
low[u] = min(low[u], dfn[v]);
}
}
sz[u] += a[u];
sum[u] = s + a[u];
if (dfn[u] == low[u]) {
cntscc ++;
int v;
do {
v = stk[top --], instk[v] = 0;
sccid[v] = cntscc;
scc[cntscc].push_back(v);
sccnum[cntscc] ++;
} while (u != v);
}
}
void work() {
for (int i = 1; i <= n; i ++) {
if (!dfn[i]) {
dfncnt = 0;
tarjan(i);
}
}
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
SCC scc(n);
for (int i = 1; i <= m; i ++) {
int u, v;
cin >> u >> v;
scc.add(u, v);
scc.add(v, u);
}
for (int i = 1; i <= n; i ++) {
cin >> a[i];
}
scc.work();
int cnt = scc.cntscc;
auto g = scc.scc;
auto rt = scc.sccid;
i128 res = 0;
vector<i128> s(cnt + 1);
for (int i = 1; i <= cnt; i ++) {
i128 x = 0;
for (auto v : g[i]) {
x += a[v];
}
res += x * x;
s[i] = x;
}
i128 Ans = res;
for (int i = 1; i <= n; i ++) {
ans[i] += res;
ans[i] -= s[rt[i]] * s[rt[i]];
ans[i] += (s[rt[i]] - sum[i]) * (s[rt[i]] - sum[i]);
Ans = min(Ans, ans[i]);
}
cout << Ans << '\n';
return 0;
}
B-小雷的神奇電腦_河南萌新聯賽2024第(四)場:河南理工大學 (nowcoder.com)
思路
程式碼
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int N = 2e5 + 5, M = 3e6 + 5;
int n,m, a[N], son[M][2], cnt;
void insert(int x) {
int p = 0;
for (int i = m - 1; ~i; i--) {
int &s = son[p][x >> i & 1];
if (!s) s = ++cnt;
p = s;
}
}
int query(int x) {
int ans = 0, p = 0;
for (int i = m - 1; ~i; i--) {
int s = x >> i & 1;
if (son[p][s]) ans += 1 << i, p = son[p][s];
else p = son[p][!s];
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
int ans = 0;
for (int i = 0; i < n; i++) {
cin >> a[i];
ans = max(ans, query(a[i]));
insert(a[i]);
}
cout << ans << '\n';
return 0;
}
思路
程式碼
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i ++) {
int x;
cin >> x;
m -= x;
}
cout << comb.C(m + n, n) << '\n';
return 0;
}
思路
程式碼
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve() {
i64 x;
cin >> x;
if(x==2){
cout << "Yes\n";
return ;
}
for (int i = 2; i <= sqrt(x); i ++) {
if (x % i == 0) {
cout << "No\n";
return ;
}
}
cout << "Yes\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
思路
程式碼
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
#define div(a, b) (1.0 * (a) / (b))
#define half(x) (((x) - 1) / 2)
i64 Meissel_Lehmer(i64 n) {
if (n <= 3) {
return max(n - 1, 0LL);
}
long long v = sqrtl(n);
int s = (v + 1) / 2;
vector<int> smalls(s), roughs(s);
vector<i64> larges(s);
for (int i = 0 ; i < s ; i++) {
smalls[i] = i;
}
for (int i = 0 ; i < s ; i++) {
roughs[i] = i * 2 + 1;
}
for (int i = 0 ; i < s ; i++) {
larges[i] = half(n / roughs[i]);
}
vector<bool> skip(v + 1);
int pc = 0;
for (int p = 3 ; p <= v ; p += 2) {
if (skip[p] == false) {
i64 q = p * p;
if (q * q > n) {
break;
}
skip[p] = true;
for (int i = q ; i <= v ; i += 2 * p) {
skip[i] = true;
}
int ns = 0;
for (int k = 0 ; k < s ; k++) {
int i = roughs[k];
if (skip[i]) {
continue;
}
long long d = 1LL * i * p;
larges[ns] = larges[k] - (d <= v ? larges[smalls[d >> 1] - pc] : smalls[half(div(n, d))]) + pc;
roughs[ns++] = i;
}
s = ns;
for (int i = half(v), j = (((v / p) - 1) | 1) ; j >= p ; j -= 2) {
int c = smalls[j / 2] - pc;
for (int e = j * p / 2 ; i >= e ; i--) {
smalls[i] -= c;
}
}
pc++;
}
}
larges[0] += 1LL * (s + 2 * (pc - 1)) * (s - 1) >> 1;
for (int k = 1 ; k < s ; k++) {
larges[0] -= larges[k];
}
for (int L = 1 ; L < s ; L++) {
int q = roughs[L];
long long m = n / q;
int e = smalls[half(m / q)] - pc;
if (e < L + 1) {
break;
}
long long t = 0;
for (int k = L + 1 ; k <= e ; k++) {
t += smalls[half(div(m, roughs[k]))];
}
larges[0] += t - 1LL * (e - L) * (pc + L - 1);
}
return larges[0] + 1;
}
#undef div
#undef half
void solve() {
int x, y;
cin >> x >> y;
int ans = Meissel_Lehmer(y) - Meissel_Lehmer(x - 1);
cout << ans << ' ';
cout << (x > 2 ? 0 : max(0, ans - 2)) << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
思路
程式碼
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
string s;
cin >> s;
vector<i64> num;
for (int i = 0; i < s.size(); i ++) {
if (s[i] != '+') {
i64 j = i, x = s[i] - '0';
while (j + 1 < s.size() && s[j + 1] != '+') {
j ++;
x = x * 10 + s[j] - '0';
}
num.emplace_back(x);
i = j;
}
}
sort(num.begin(), num.end(), greater<>());
i64 ans = 0;
for (int i = 0; i < num.size(); i ++) {
ans += num[i];
cout << num[i];
if (i != num.size() - 1)cout << '+';
}
cout << '\n' << ans << '\n';
return 0;
}
思路
程式碼
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using u64 = unsigned long long;
constexpr int p = 13331, M = 2e6 + 10;
u64 P[M], hs[M], f[M];
template<class Node>
struct SegmentTree {
#define lc u<<1
#define rc u<<1|1
const int n, N;
vector<Node> tr;
SegmentTree(): n(0) {}
SegmentTree(int n_): n(n_), N(n * 4 + 10) {
tr.reserve(N);
tr.resize(N);
}
SegmentTree(vector<int> init) : SegmentTree(init.size()) {
function<void(int, int, int)> build = [&](int u, int l, int r) {
tr[u].l = l, tr[u].r = r;
init_lazy(tr[u]);
if (l == r) {
tr[u] = {l, r, (u64)init[l], (u64)init[l], (u64)0};
return ;
}
i64 mid = (l + r) >> 1;
build(lc, l, mid);
build(rc, mid + 1, r);
pushup(tr[u], tr[lc], tr[rc]);
};
build(1, 1, n);
}
void cal_lazy(Node & fa, Node & ch) {
ch.h[0] = ch.h[1] = fa.lazy * f[(ch.r - ch.l)];
}
void tag_union(Node& fa, Node& ch) {
ch.lazy = fa.lazy;
}
void init_lazy(Node& u) {
u.lazy = 0;
}
void pushdown(i64 u) {
if (tr[u].lazy != 0) {
cal_lazy(tr[u], tr[lc]);
cal_lazy(tr[u], tr[rc]);
tag_union(tr[u], tr[lc]);
tag_union(tr[u], tr[rc]);
init_lazy(tr[u]);
}
}
void pushup(Node& U, Node& L, Node& R) { //上傳
U.l = L.l;
U.r = R.r;
U.h[0] = L.h[0] * P[R.r - R.l + 1] + R.h[0];
U.h[1] = R.h[1] * P[L.r - L.l + 1] + L.h[1];
}
void modify(int u, int l, int r, int k) {
if (tr[u].l >= l && tr[u].r <= r) {
tr[u].lazy = k;
tr[u].h[0] = tr[u].h[1] = k * f[(tr[u].r - tr[u].l)];
return ;
}
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if (l <= mid)
modify(lc, l, r, k);
if (r > mid)
modify(rc, l, r, k);
pushup(tr[u], tr[lc], tr[rc]);
}
Node query(int u, int l, int r) { //區查
if (l <= tr[u].l && tr[u].r <= r)
return tr[u];
int mid = tr[u].l + tr[u].r >> 1;
pushdown(u);
if (r <= mid)
return query(lc, l, r);
if (l > mid)
return query(rc, l, r);
Node U;
Node L = query(lc, l, r), R = query(rc, l, r);
pushup(U, L, R);
return U;
}
};
struct Node { //線段樹定義
i64 l, r;
u64 h[2];
u64 lazy;
};
vector<int> euler_range(int n) {
vector<int> phi(n + 1), prime;
vector<bool> is_prime(n + 1, true);
is_prime[1] = 0, phi[1] = 1;
for (int i = 2; i <= n; i++) {
if (is_prime[i]) prime.push_back(i), phi[i] = i;
for (int j = 0; j < (int)prime.size() && i * prime[j] <= n; j++) {
is_prime[i * prime[j]] = 0, phi[i * prime[j]] = prime[j];
if (i % prime[j] == 0) break;
}
}
return phi;
}
auto fac = euler_range(M);
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
string s;
cin >> s;
P[0] = f[0] = 1;
for (int i = 1; i <= M - 10; i ++) {
P[i] = P[i - 1] * p, f[i] = f[i - 1] + P[i];
}
vector<int> a(n + 1);
for (int i = 1; i <= n; i ++) {
a[i] = s[i - 1] - 'a' + 1;
}
SegmentTree<Node> tr(a);
while (m --) {
int op, l, r;
cin >> op >> l >> r;
if (!op) {
char c;
cin >> c;
tr.modify(1, l, r, c - 'a' + 1);
} else {
i64 len = r - l + 1, ans = len, idx = op - 1;
if (l == r || tr.query(1, l + 1, r).h[idx] == tr.query(1, l, r - 1).h[idx]) {
ans = 1;
} else {
for (; len > 1; len /= fac[len]) {
if (tr.query(1, l + ans / fac[len], r).h[idx] == tr.query(1, l, r - ans / fac[len]).h[idx])
ans /= fac[len];
}
}
cout << ans << '\n';
}
}
return 0;
}
思路
程式碼
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve() {
int x, n;
cin >> x >> n;
cout << x - (n - 1) / 2 << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
思路
程式碼
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
struct DSU {
std::vector<int> f, siz;
DSU() {}
DSU(int n) {
init(n);
}
void init(int n) {
f.resize(n);
std::iota(f.begin(), f.end(), 0);
siz.assign(n, 1);
}
int find(int x) {
while (x != f[x]) {
x = f[x] = f[f[x]];
}
return x;
}
bool same(int x, int y) {
return find(x) == find(y);
}
bool merge(int x, int y) {
x = find(x);
y = find(y);
if (x == y) {
return false;
}
siz[x] += siz[y];
f[y] = x;
return true;
}
int size(int x) {
return siz[find(x)];
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, x, y;
cin >> n >> x >> y;
vector g(n + 1, vector<int>());
for (int i = 1; i < n; i ++) {
int u, v;
cin >> u >> v;
g[u].emplace_back(v);
g[v].emplace_back(u);
}
DSU dsu;
dsu.init(n + 1);
vector<int> sz(n + 1);
auto dfs = [&](auto & self, int u, int fa)->void{
sz[u] = 1;
for (auto v : g[u]) {
if (v == fa) continue;
self(self, v, u);
if (u != fa) {
dsu.merge(u, v);
}
sz[u] += sz[v];
}
};
dfs(dfs, x, x);
i64 a = sz[x], b = sz[y];
for (auto v : g[x]) {
if (dsu.same(v, y)) {
a -= sz[v];
break;
}
}
cout << a * b << '\n';
return 0;
}
思路
程式碼
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int N = 1e5 + 10, M = N - 10;
vector<int> f(N);
struct LCA {
int n;
vector<int> dep;
vector<vector<int>> e;
vector<array<int, 21>> fa;
LCA() {}
LCA(int n) {
dep.resize(n + 1);
e.resize(n + 1);
fa.resize(n + 1);
}
void add(int u, int v) {
e[u].push_back(v);
e[v].push_back(u);
}
//計算深度,處理各點祖先
void dfs(int u, int father) {
dep[u] = dep[father] + 1;
fa[u][0] = father;
for (int i = 1; i < 20; i ++)
fa[u][i] = fa[fa[u][i - 1]][i - 1];
for (auto v : e[u])
if (v != father)
dfs(v, u);
}
//最近公共祖先
//兩點集並的最近公共祖先為兩點幾分別的最近公共祖先的最近公共祖先,
//即LCA(A∪B) = LCA(LCA(A), LCA(B));
int lca(int u, int v) {
if (dep[u] < dep[v]) swap(u, v);
for (int i = 19; i >= 0; i --)
if (dep[fa[u][i]] >= dep[v])
u = fa[u][i];
if (u == v) return v;
for (int i = 19; i >= 0; i --)
if (fa[u][i] != fa[v][i])
u = fa[u][i], v = fa[v][i];
return fa[u][0];
}
//d(u,v) = h(u) + h(v) - 2h(LCA(u,v));
//其中d是樹上兩點間的距離,h代表某點到樹根的距離
int get_dis(int u, int v) {
return dep[u] + dep[v] - 2 * dep[lca(u, v)];
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int pos = 0;
f[1] = 1, f[2] = 2;
for (int i = 3; i <= M; i ++) {
f[i] = f[i - 1] + f[i - 2];
if (f[i] > 1e5) {
pos = i;
break;
}
}
int n, r, q;
cin >> n >> r >> q;
LCA tr(n);
for (int i = 1; i < n; i ++) {
int u, v;
cin >> u >> v;
tr.add(u, v);
}
tr.dfs(r, 0);
while (q--) {
int x, k;
cin >> x >> k;
vector<int> node;
while (k <= pos && x + f[k] <= n) {
node.emplace_back(x + f[k]);
k ++;
}
if (!node.size()) {
cout << 0 << '\n';
continue;
}
int ans = node[0];
for (int i = 1; i < node.size(); i ++) {
ans = tr.lca(ans, node[i]);
}
cout << ans << '\n';
}
return 0;
}
思路
程式碼
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
template<typename T>
struct BIT {
#ifndef lowbit
#define lowbit(x) (x & (-x));
#endif
int n;
vector<T> t;
BIT () {}
BIT (int _n): n(_n) { t.resize(_n + 1); }
BIT (int _n, vector<T>& a): n(_n) {
t.resize(_n + 1);
for (int i = 1; i <= n; ++ i) {
t[i] += a[i];
int j = i + lowbit(i);
if (j <= n) t[j] += t[i];
}
}
//單點修改
void update(int i, T x) {
while (i <= n) {
t[i] += x;
i += lowbit(i);
}
}
//區間查詢
T sum(int i) {
T ans = 0;
while (i > 0) {
ans += t[i];
i -= lowbit(i);
}
return ans;
}
T query(int i, int j) {
return sum(j) - sum(i - 1);
}
//區間修改則存入差分陣列,[l, r] + k則update(x,k),update(y+1,-k)
//單點查詢則直接求字首和sum(x)
//求逆序對
/*
iota(d.begin(), d.end(), 0);
stable_sort(d.begin(), d.end(), [&](int x, int y) {
return a[x] < a[y];
});去重排序
BIT<i64> tree(n);
i64 ans = 0;
for (int i = 1; i <= n; i ++) {
tree.update(d[i], 1);
ans += i - tree.sum(d[i]);
}
*/
};
void solve() {
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i ++) {
cin >> a[i];
}
const int N = 1e5 + 10, M = N - 10;
BIT<i64> bit1(N), bit2(N);
for (int i = 3; i <= n; i ++) {
bit2.update(a[i], 1);
}
bit1.update(a[1], 1);
i64 ans = 0;
for (int j = 2; j < n; j ++) {
if (j > 2)
bit2.update(a[j], -1);
ans += 1ll * bit1.sum(a[j]) * (bit2.query(a[j], M));
ans += 1ll * bit2.sum(a[j]) * (bit1.query(a[j], M));
ans -= 1ll * bit1.query(a[j], a[j]) * bit2.query(a[j], a[j]);
if (j < n - 1)
bit1.update(a[j], 1);
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
思路
程式碼