今天的題不算難,但是沒做出一題,有點失敗。
A
你打完表之後發現並沒有什麼出色的性質。只能考慮爆搜。
程式碼好寫,但是你要分析複雜度。
最關鍵的一點是每一次遞迴至少多一個 \(1\),而 \(1\) 可以直接 return,所以最多遞迴 \(m\) 次就夠了。
#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define mk std::make_pair
#define fi first
#define se second
#define pb push_back
using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
/*
*/
i64 x, k, m, ans;
std::vector<i64> v;
void dfs(i64 now, i64 l) {
if(!m) return;
if(now == 1 || !l) {
ans += now;
m--;
return;
}
for(i64 c : v) {
if(c > now) break;
if(!(now % c)) dfs(c, l - 1);
if(!m) return;
}
}
int main() {
freopen("trans.in", "r", stdin);
freopen("trans.out", "w", stdout);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> x >> k >> m;
if(k >= m) {
std::cout << m << "\n";
return 0;
}
for(i64 i = 1; i * i <= x; i++) {
if(!(x % i)) {
v.pb(i);
if(x / i != i) v.pb(x / i);
}
}
std::sort(v.begin(), v.end());
k = std::min(k, m);
dfs(x, k);
std::cout << ans << "\n";
return 0;
}
B
顯然除了關鍵的 \(\log n\) 個點其他位置等價,可以組合數計算。假如最終函式停止時左邊的關鍵點有 \(L\) 個,右邊有 \(R\) 個,停止時是否找到 \(x\) 標記為 \(E\)。那麼對一個三元組 \((L,R,E)\),考慮一個 \(y<x\) 的貢獻:
\(y> x\) 與 \(y=x\) 類似。
可以發現三元組與 \(x\) 無關,考慮一次 dfs 預處理出所有三元組,每次詢問列舉每個三元組求出每個的答案即可。
複雜度 \(O(n\log n+q\log^2n)\)。
#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define mk std::make_pair
#define fi first
#define se second
#define pb push_back
using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
/*
*/
const int N = 3e5 + 10, mod = 1e9 + 7, inv2 = (mod + 1) / 2;
i64 n, q;
std::map<std::array<i64, 3>, i64> mp;
i64 fac[N], inv[N], ans;
void dfs(i64 l, i64 r, i64 ls, i64 rs) {
if(l > r) {
mp[{ls, rs, 0}]++;
return;
}
mp[{ls, rs, 1}]++;
int mid = (l + r) >> 1;
dfs(l, mid - 1, ls, rs + 1), dfs(mid + 1, r, ls + 1, rs);
}
i64 qpow(i64 a, i64 b) {
i64 ret = 1;
while(b) {
if(b & 1) ret = ret * a % mod;
a = a * a % mod;
b >>= 1;
}
return ret;
}
void init() {
fac[0] = 1;
for(int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % mod;
inv[n] = qpow(fac[n], mod - 2);
for(int i = n - 1; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1) % mod;
}
i64 C(i64 n, i64 m) {
if(m > n) return 0;
return fac[n] * inv[m] % mod * inv[n - m] % mod;
}
i64 sum(i64 l, i64 r) {
return (l + r) * (r - l + 1) % mod * inv2 % mod;
}
int main() {
freopen("binary.in", "r", stdin);
freopen("binary.out", "w", stdout);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n >> q;
dfs(1, n, 0, 0);
init();
while(q--) {
i64 x;
std::cin >> x;
ans = 0;
for(auto c : mp) {
i64 L = c.fi[0], R = c.fi[1], E = c.fi[2], cnt = c.se;
if(L) ans = (ans + cnt % mod * sum(1, x - 1) % mod * L % mod * C(x - 2, L - 1) % mod * C(n - x, R) % mod * fac[L - 1] % mod * fac[R] % mod * fac[n - L - R - E] % mod) % mod;
if(R) ans = (ans + cnt % mod * sum(x + 1, n) % mod * R % mod * C(n - x - 1, R - 1) % mod * C(x - 1, L) % mod * fac[R - 1] % mod * fac[L] % mod * fac[n - L - R - E] % mod) % mod;
if(E) ans = (ans + cnt % mod * x % mod * C(x - 1, L) % mod * fac[L] % mod * C(n - x, R) % mod * fac[R] % mod * fac[n - L - R - E] % mod) % mod;
}
std::cout << ans << "\n";
}
return 0;
}
C
顯然的做法,每個操作看成一條邊,那麼在一個連通塊內的點內部一定能夠排序成從小到大的形態,所有連通塊拼起來就是最小字典序。問題出在邊數是 \(O(n^2)\) 的。考慮最佳化建邊。
我們不關心邊是什麼,我們只關心連通性,觀察到值域只有 \(2^{20}\),考慮在值域上建邊。
每個操作可以看成連向他的補集,然後補集連向他所有的子集。這樣建圖在新圖上無向邊的連通塊就變成了強連通分量,跑一遍 tarjan 輸出答案即可。
複雜度 \(O(n\log n)\)。
#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define mk std::make_pair
#define fi first
#define se second
#define pb push_back
using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
/*
*/
const int N = 2e6 + 10;
int n;
int a[N];
int dfn[N], low[N];
int ins[N];
int st[N];
int bel[N];
int cnt[N];
int vis[N];
int pos[N];
std::vector<int> e[N], ans[N];
std::vector<pii> g[N];
int top, idx, tot;
void tarjan(int u) {
st[++top] = u, ins[u] = 1;
dfn[u] = low[u] = ++tot;
for(int v : e[u]) {
if(!dfn[v]) {
tarjan(v);
low[u] = std::min(low[u], low[v]);
} else if(ins[v]) {
low[u] = std::min(low[u], dfn[v]);
}
}
if(low[u] == dfn[u]) {
++idx;
int v;
do {
v = st[top--];
if(cnt[v]) {
g[idx].pb({v, cnt[v]});
}
bel[v] = idx;
ins[v] = 0;
} while(v != u);
}
}
int main() {
freopen("seq.in", "r", stdin);
freopen("seq.out", "w", stdout);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n;
int lim = (1 << 20) - 1;
for(int i = 1; i <= n; i++) {
std::cin >> a[i];
cnt[a[i]]++;
e[a[i]].pb(lim ^ a[i]);
}
for(int i = 1; i <= lim; i++) {
for(int j = 0; j < 20; j++) {
if((i >> j) & 1) {
e[i].pb(i ^ (1 << j));
}
}
}
for(int i = 0; i <= lim; i++) {
if(!dfn[i]) tarjan(i);
}
for(int i = 1; i <= idx; i++) {
if(!g[i].size()) continue;
std::sort(g[i].begin(), g[i].end());
for(int j = 0; j < g[i].size(); j++) {
for(int k = 1; k <= g[i][j].se; k++) ans[i].pb(g[i][j].fi);
}
}
for(int i = 1; i <= n; i++) {
std::cout << ans[bel[a[i]]][pos[bel[a[i]]]++] << " ";
}
std::cout << "\n";
return 0;
}
D
簡單的是 \(O(n^2\log n)\) 的暴力,列舉兩個點計算答案。但這是沒有前途的。
考慮列舉顏色,把式子寫出來。
把 \(K\) 提出,然後整理好。
考慮前面括號的部分的處理,實際上可以寫成:
如果設 \(s_0=\sum\frac{d_i}{k_i}\),\(s1=\sum\frac{1}{k_i}\),那麼 \(s_0\cdot s_1\) 基本為上式(只不過要減去 \(i=j\) 的部分),所以從小到大列舉 \(c\) 的時候掃描線,維護這兩個值即可。
\(lca\) 的部分用上經典的 trick,將式子拆成:\(\frac{2d_{lca(i,j)}}{k_i}\cdot\frac{1}{k_j}\)。前者直接作為 \(i\) 到根的路徑貢獻到每一條邊。當插入 新的點 \(j\) 時,查詢 \(j\) 到根的路徑上的和再乘上 \(\frac{1}{k_j}\) 即為貢獻,這部分可以樹剖+線段樹維護區間和。
複雜度 \(O(n\log n)\)。
#include <bits/stdc++.h>
#define pii std::pair<i64, i64>
#define mk std::make_pair
#define fi first
#define se second
#define pb push_back
using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
/*
*/
const int N = 1e5 + 10, mod = 1e9 + 7;
i64 ans, K = 1;
int n, idx, maxn;
int l[N], r[N];
int id[N];
int top[N];
int sz[N];
int fa[N];
int len[N];
i64 inv[N];
int son[N];
i64 dep[N];
std::vector<pii> v[N];
std::vector<int> e[N];
i64 qpow(i64 a, i64 b) {
i64 ret = 1;
while(b) {
if(b & 1) ret = ret * a % mod;
a = a * a % mod;
b >>= 1;
}
return ret;
}
void dfs1(int u, int f) {
dep[u] = dep[f] + 1;
fa[u] = f, sz[u] = 1;
for(auto v : e[u]) {
if(v == f) continue;
dfs1(v, u);
sz[u] += sz[v];
if(sz[son[u]] < sz[v]) son[u] = v;
}
}
void dfs2(int u, int topf) {
id[u] = ++idx;
top[u] = topf;
if(!son[u]) return;
dfs2(son[u], topf);
for(auto v : e[u]) {
if(v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
struct seg {
i64 v, tg;
} t[N << 2];
void pushup(int u) {t[u].v = (t[u << 1].v + t[u << 1 | 1].v + mod) % mod;}
void mdf(seg &u, i64 v, int l, int r) {
u.v = (u.v + (r - l + 1) * v % mod) % mod;
u.tg = (u.tg + v) % mod;
}
void pd(int u, int l, int r) {
if(!t[u].tg) return;
int mid = (l + r) >> 1;
mdf(t[u << 1], t[u].tg, l, mid);
mdf(t[u << 1 | 1], t[u].tg, mid + 1, r);
t[u].tg = 0;
}
i64 ask(int u, int l, int r, int L, int R) {
if(L <= l && r <= R) {
return t[u].v;
}
int mid = (l + r) >> 1; pd(u, l, r);
i64 ret = 0;
if(L <= mid) ret = (ret + ask(u << 1, l, mid, L, R) + mod) % mod;
if(R > mid) ret = (ret + ask(u << 1 | 1, mid + 1, r, L, R) + mod) % mod;
return ret;
}
void add(int u, int l, int r, int L, int R, i64 x) {
if(L <= l && r <= R) {
mdf(t[u], x, l, r);
return;
}
int mid = (l + r) >> 1; pd(u, l, r);
if(L <= mid) add(u << 1, l, mid, L, R, x);
if(R > mid) add(u << 1 | 1, mid + 1, r, L, R, x);
pushup(u);
}
void upd(int u, int v) {
while(u) {
add(1, 1, n, id[top[u]], id[u], v);
u = fa[top[u]];
}
}
i64 qry(int u) {
i64 ret = 0;
while(u) {
ret = (ret + ask(1, 1, n, id[top[u]], id[u]) + mod) % mod;
u = fa[top[u]];
}
return ret;
}
int main() {
freopen("route.in", "r", stdin);
freopen("route.out", "w", stdout);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n;
for(int i = 1; i <= n; i++) {
std::cin >> l[i] >> r[i];
v[l[i]].pb({i, 1});
v[r[i] + 1].pb({i, -1});
len[i] = r[i] - l[i] + 1;
inv[i] = qpow(len[i], mod - 2);
K = K * len[i] % mod;
maxn = std::max(maxn, r[i]);
}
for(int i = 1; i < n; i++) {
int u, v;
std::cin >> u >> v;
e[u].pb(v), e[v].pb(u);
}
dfs1(1, 0), dfs2(1, 1);
i64 s0 = 0, s1 = 0, s2 = 0, s3 = 0;
for(int i = 1; i <= maxn; i++) {
for(auto x : v[i]) {
i64 c = x.fi;
if(x.se == -1) {
s0 = (s0 - (dep[c] * inv[c] % mod + mod) % mod) % mod;
s1 = (s1 - inv[c] + mod) % mod;
s2 = (s2 - (dep[c] * inv[c] % mod * inv[c] % mod + mod) % mod) % mod;
upd(c, mod - inv[c]);
s3 = (s3 - inv[c] * qry(c) % mod + mod) % mod;
} else {
s0 = (s0 + dep[c] * inv[c] % mod + mod) % mod;
s1 = (s1 + inv[c] % mod) % mod;
s2 = (s2 + dep[c] % mod * inv[c] % mod * inv[c] % mod) % mod;
s3 = (s3 + inv[c] * qry(c) % mod) % mod;
upd(c, inv[c]);
}
}
ans = (ans + K * (((s0 * s1 % mod) - s2 + mod) % mod - 2ll * s3 + 2ll * mod) % mod) % mod;
}
std::cout << ans << "\n";
return 0;
}