闲话 22.11.15
闲话
幸亏昨天写了两道题 要不然今天没题可写了(
明天学考?
话说怎么那么多 bitmask 题啊
杂题
Cyberland 有 \(n\) 座城市,编号从 \(1\) 到 \(n\),有 \(m\) 条双向道路连接这些城市。第 \(j\) 条路连接城市 \(a_j\) 和 \(b_j\)。每天,都有成千上万的游客来到 Cyberland 游玩。在每一个城市,都有纪念品售卖,第 \(i\) 个城市售价为 \(w_i\)。这个售价有时会变动。
每一个游客的游览路径都有固定起始城市和终止城市,且不会经过重复的城市。他们会在路径上的城市中,售价最低的那个城市购买纪念品。你能求出每一个游客在所有合法的路径中能购买的最低售价是多少吗?
你要处理 \(q\) 个操作:
C a w
: 表示 \(a\) 城市的纪念品售价变成 \(w\)。
A a b
: 表示有一个游客要从 \(a\) 城市到 \(b\) 城市,你要回答在所有他的旅行路径中最低售价的最低可能值。\(1\le n, m, q \le 10^5\)。
经典图论题。
首先考虑树上怎么做。显然树剖求路径最小值 \(O(n\log ^2 n)\)。
然后考虑扔到图上。我们需要转化成树上问题。
建立广义圆方树,圆点权值是自己,方点权值是它链接的点的权值……吗?
考虑一棵菊花树。我们发现一个圆点可以链接一堆方点。这样直接修改单次可能会改动 \(O(n)\) 个方点的信息。这是不优的。
不妨令 \(1\) 为根,钦定一个树上父子关系。我们令一个方点维护自己儿子的信息,当路径 LCA 是方点时加入 LCA 的父亲的信息。
总时间复杂度 \(O(n \log^2 n)\)。
code
#include <bits/stdc++.h>
using namespace std; using pii = pair<int,int>;
using ll = long long; using ull = unsigned long long; using db = double; using ld = long double; using lll = __int128_t;
mt19937_64 rnd(chrono::steady_clock::now().time_since_epoch().count());
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
const int N = 1e5 + 10;
const int inf = 0x3f3f3f3f;
int n, m, q, w[N << 1], t1, t2;
char typ;
vector<int> gr[N], tr[N << 1];
int dfn[N << 1], low[N], stp, cnt;
int stk[N], tp;
void tarjan(int u) {
dfn[u] = low[u] = ++ stp; stk[++tp] = u;
for (auto v : gr[u]) {
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u]) {
int t = -1; ++ cnt;
do {
t = stk[tp --];
tr[t].emplace_back(cnt); tr[cnt].emplace_back(t);
} while (t != v);
tr[u].emplace_back(cnt); tr[cnt].emplace_back(u);
}
} else
low[u] = min(low[u], dfn[v]);
}
}
int fa[N << 1], dep[N << 1], idfn[N << 1], siz[N << 1], son[N << 1], top[N << 1];
void find_hc(int u, int f) {
dep[u] = dep[f] + 1, fa[u] = f, siz[u] = 1;
for (auto v : tr[u]) if (v != f) {
find_hc(v, u); siz[u] += siz[v];
if (siz[v] > siz[son[u]]) son[u] = v;
}
}
void con_hc(int u, int tp) {
dfn[u] = ++ stp; idfn[stp] = u; top[u] = tp;
if (son[u]) con_hc(son[u], tp);
for (auto v : tr[u]) if (v != fa[u] and v != son[u]) con_hc(v, v);
}
multiset<int> st[N];
class SegmentCitrus {
#define ls (p << 1)
#define rs (p << 1 | 1)
int seg[N << 3];
void ps_p(int p) { seg[p] = min(seg[ls], seg[rs]); }
void build(int p, int l, int r) {
if (l == r) {
seg[p] = w[idfn[l]];
return;
} int mid = l + r >> 1;
build(ls, l, mid); build(rs, mid+1, r);
ps_p(p);
}
void update(int p, int l, int r, int pos, int val) {
if (l == r) {
seg[p] = val;
return;
} int mid = l + r >> 1;
if (pos <= mid) update (ls, l, mid, pos, val);
else update (rs, mid + 1, r, pos, val);
ps_p(p);
}
int query(int p, int l, int r, int L, int R) {
if (L <= l and r <= R) return seg[p];
int mid = l + r >> 1, ret = inf;
if (L <= mid) ret = min(ret, query(ls, l, mid, L, R));
if (mid < R) ret = min(ret, query(rs, mid+1, r, L, R));
return ret;
}
int query(int l, int r) { return query(1, 1, cnt, l, r); }
public :
void build() { build(1, 1, cnt); }
int path(int u, int v) {
int ret = inf;
while (top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) swap(u, v);
ret = min(ret, query(dfn[top[u]], dfn[u]));
u = fa[top[u]];
} if (dep[u] > dep[v]) swap(u, v);
ret = min(ret, query(dfn[u], dfn[v]));
if (u > n) ret = min(ret, w[fa[u]]);
return ret;
}
void update(int p, int v) { update(1, 1, cnt, dfn[p], v); }
} Tr;
void update(int u, int val) {
Tr.update(u, val);
if (u == 1) {
w[u] = val;
return;
} st[fa[u] - n].erase(st[fa[u] - n].find(w[u]));
st[fa[u] - n].insert(val); w[u] = val;
if (w[fa[u]] != *st[fa[u] - n].begin())
Tr.update(fa[u], *st[fa[u] - n].begin()), w[fa[u]] = *st[fa[u] - n].begin();
}
signed main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m >> q; rep(i,1,n) cin >> w[i];
rep(i,1,m) cin >> t1 >> t2, gr[t1].emplace_back(t2), gr[t2].emplace_back(t1);
cnt = n; rep(i,1,n) if (!dfn[i]) tarjan(i);
stp = 0; find_hc(1, 0); con_hc(1, 1);
rep(i,2,n) st[fa[i] - n].emplace(w[i]);
rep(i,n+1,cnt) w[i] = st[i - n].size() ? *st[i - n].begin() : inf;
Tr.build();
while (q--) {
cin >> typ >> t1 >> t2;
if (typ == 'C') {
update(t1, t2);
} else {
cout << Tr.path(t1, t2) << '\n';
}
}
}
[IPSC2016] Counting Swaps
给定你一个 \(1∼n\) 的排列 \(p\),可进行若干次操作,每次选择两个整数 \(x,y\),交换 \(p_x,\ p_y\)。请输出用最少的操作次数将给定排列变成单调上升的序列的操作方案数对 \(10^9+9\) 取模的结果。
记置换环数为 \(\text{cnt}\),第 \(i\) 个环的大小为 \(s_i\)。
操作方案比较典。\(n - \text{cnt}\) 就是答案。
然后首先有各个环操作彼此独立,先乘进一个 \(\frac{(n-\text{cnt})!}{\prod_{i=1}^{\text{cnt}}(s_i-1)!}\),然后我们只需要乘入每个环的可能性。
对于一个环 \(i\),考虑使用图论模型计数。交换其中两个元素 \(p_x,\ p_y\) 后加一条边 \((x,y)\)。交换 \(s_i-1\) 次,两个元素不会被重复交换,且不会交换相同元素。这给出了一棵有标号无根树。因此对不同的操作方案计数树得到了总可能的方案数 \(s_i^{s_i-2}\)。
乘起来得到总方案数
计算即可。总时间复杂度 \(O(n)\)。
code
const int N = 5e5 + 10;
const int inf = 0x3f3f3f3f;
const ll infll = 0x3f3f3f3f3f3f3f3fll;
const int mod = 1e9 + 9;
const int inv2 = (mod + 1) >> 1;
template <typename T1, typename T2> T1 add(T1 a, T2 b) { return (a += b) >= mod ? a - mod : a; }
template <typename T1, typename ...Args> T1 add(T1 a, Args ... b) { return add(a, add(b...)); }
struct FastMod { int m; ll b; void init(int _m) { m = _m; if (m == 0) m = 1; b = ((lll)1<<64) / m; } FastMod(int _m) { init(_m); } int operator() (ll a) {ll q = ((lll)a * b) >> 64; a -= q * m; if (a >= m) a -= m; return a; } } Mod(mod);
int mul(int a, int b) { return Mod(1ll * a * b); } template <typename ...Args> int mul(int a, Args ...b) { return mul(a, mul(b...)); }
template <typename T1, typename T2> T1 qp(T1 a, T2 b) { T1 ret = 1; for (; b > 0; a = mul(a, a), b >>= 1) if (b & 1) ret = mul(ret, a); return ret; }
int T, n, a[N], siz[N], cnt, fac[N], inv[N], ans = 1;
bool vis[N];
int main() {
get(T);
fac[0] = fac[1] = inv[0] = inv[1] = 1;
rep(i,2,100000) fac[i] = mul(i, fac[i - 1]), inv[i] = mul(mod - mod / i, inv[mod % i]);
rep(i,2,100000) inv[i] = mul(inv[i], inv[i - 1]);
while (T--) {
get(n); rep(i,1,n) get(a[i]), vis[i] = false;
cnt = 0, ans = 1;
for (int i = 1, ptr; i <= n; ++ i) if (!vis[i]) {
vis[i] = 1; cnt ++; ptr = a[i]; siz[cnt] = 1;
while (ptr != i) vis[ptr] = 1, ptr = a[ptr], siz[cnt] ++;
}
rep(i,1,cnt) ans = mul(ans, qp(siz[i], siz[i] - 2), inv[siz[i] - 1]);
ans = mul(ans, fac[n - cnt]);
cout << ans << '\n';
}
}
以下是博客签名,与正文无关。
请按如下方式引用此页:
本文作者 joke3579,原文链接:https://www.cnblogs.com/joke3579/p/chitchat221115.html。
遵循 CC BY-NC-SA 4.0 协议。
请读者尽量不要在评论区发布与博客内文完全无关的评论,视情况可能删除。