今天的题不算难,但是没做出一题,有点失败。
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\) 的贡献:
\[yL{x-2\choose L-1}{n-x\choose R}(L-1)!R!(n-L-R-E)! \]\(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)\) 的暴力,枚举两个点计算答案。但这是没有前途的。
考虑枚举颜色,把式子写出来。
\[\sum\limits_{c}\sum\limits_{c\in[l_i,r_i]}\sum\limits_{c\in[l_j,r_j]}\frac{K}{k_ik_j}(d_i+d_j-2d_{lca(i,j)}) \]把 \(K\) 提出,然后整理好。
\[\sum\limits_{c}K\sum\limits_{c\in[l_i,r_i]}\sum\limits_{c\in[l_j,r_j]}(\frac{d_i}{k_ik_j}+\frac{d_j}{k_ik_j})-\frac{2d_{lca(i,j)}}{k_ik_j} \]考虑前面括号的部分的处理,实际上可以写成:
\[\frac{d_i}{k_i}\cdot\frac{1}{k_j}+\frac{d_j}{k_j}\cdot\frac{1}{k_i} \]如果设 \(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;
}
标签:std,AB,0731,--,long,i64,int,define,mod
From: https://www.cnblogs.com/FireRaku/p/18339586