闲话 22.11.21

闲话

最近杂题的讲解部分越来越少了是我的错觉吗?
最近闲话越来越少了是我的错觉吗?

怎么没东西可写了?

对了还三天 noip 退役感 ++
前两天晚上以为 noip 考不了了 当晚退役感满满
有人跳我脸说 我退个**役(本人说的“星星”)
我觉得他在 fAKe

image

杂题

L572 Misaka Network 与求和

给定 \(n,k\)。求

\[\sum_{i=1}^n\sum_{j=1}^n f(gcd(i,j))^k \pmod {2^{32}} \]

其中 \(f(n)\)\(n\) 次大的质因子,重复的质因子算多次。规定 \(f(1) = 0,\ f([质数])=1\)

\(n, k\le 2\times 10^9\)

\[\begin{aligned} &\sum_{i=1}^n\sum_{j=1}^n f(gcd(i,j))^k \\ = \ & \sum_{x=1}f(x)^k\sum_{i=1}^n\sum_{j=1}^n [gcd(i, j) = x] \\ = \ & \sum_{x=1}f(x)^k\left(2\times \varphi(\left\lfloor\frac nx\right\rfloor) - 1\right) \end{aligned}\]

数论分块 + 杜教筛即可解决后半部分。现在的问题是怎么得到所有 \(\left\lfloor\frac nx\right\rfloor\) 处的 \(f^k\) 点值。

考虑洲阁筛。我们预先处理处质数处的 \(p^k\) 的值。然后辅助函数可以设为 \(\text I\)。直接筛即可。
这样做不需要搞 \(f^k * \mu\) 什么的,直接朴素地做即可。

总时间复杂度 \(O(n^{\frac 23} + \frac{n^{\frac 43}}{\log n})\).

code
#include <bits/stdc++.h>
#define int unsigned int
using namespace std; using pii = pair<int,int>; using vi = vector<int>; using ll = long long; 
using ull = unsigned long long; using db = double; using ld = long double; using lll = __int128_t;
mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());
template <typename T> T rand(T l, T r) { return uniform_int_distribution<T>(l, r)(rnd); }
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
template <typename T1, typename T2> T1 min(T1 a, T2 b) { return a < b ? a : b; }
template <typename T1, typename T2> T1 max(T1 a, T2 b) { return a > b ? a : b; }
#define fsqrt(x) (__builtin_sqrtf(x))
#define fcbrt(x) (__builtin_cbrtf(x))
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define ufile(x) 
#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)
#define Aster(i, s) for (int i = head[s], v; i; i = e[i].next)
#define all(s) s.begin(), s.end()
#define eb emplace_back
#define pb pop_back
#define em emplace
const int N = 2e6 + 100;
int n, k, ans, bnd, sq;
int id1[N], id2[N], v[N], m;
int & get(int p) { return p < sq ? id1[p] : id2[n / p]; }

int qp(int a, int b) {
	int ret = 1;
	while (b) {
		if (b & 1) ret = ret * a;
		a = a * a;
		b >>= 1;
	} return ret;
}

int p[N], cnt, phi[N], f[N], g[N], S[N];
void sieve() {
	phi[1] = 1;
	rep(i,2,bnd) {
		if (!phi[i]) p[++ cnt] = i, phi[i] = i - 1, f[cnt] = qp(i, k);
		rep(j,1,cnt) {
			if (i * p[j] > bnd) break;
			if (i % p[j] == 0) {
				phi[i * p[j]] = phi[i] * p[j];
				break;
			} else {
				phi[i * p[j]] = phi[i] * phi[p[j]];
			}
		}
	} rep(i,2,bnd) phi[i] += phi[i - 1];
}

int _phi[N];
int getPhi(int x) {
	if (x <= bnd) return phi[x];
	if (~_phi[get(x)]) return _phi[get(x)];
	int ret = 1ll * x * (x + 1) >> 1;
	for (int l = 2, r; l <= x; l = r + 1) {
		r = x / (x / l);
		ret -= (r - l + 1) * getPhi(x / l);
	} return _phi[get(x)] = ret;
}

signed main() {
	cin >> n >> k; 
	sq = fsqrt(n); bnd = pow(n, 2.0 / 3);
	sieve(); memset(_phi, -1, sizeof _phi);
	while (p[cnt] > sq) -- cnt;
	for (int l = 1, r; l <= n; l = r + 1) {
		v[++ m] = n / l; r = min(n, n / v[m]);
		get(n / l) = m; g[m] = v[m] - 1;
	}
	rep(i,1,cnt) for (int j = 1, lim = p[i] * p[i]; j <= m and lim <= v[j]; ++ j) {
		g[j] -= g[get(v[j] / p[i])] - (i - 1);
	} 
	pre(i,cnt,1) for (int j = 1, lim = p[i] * p[i]; j <= m and lim <= v[j]; ++ j) {
		for (int P = p[i]; 1ll * P * p[i] <= v[j]; P *= p[i]) {
			int k = get(v[j] / P);
			S[j] += S[k] + f[i] * (g[k] - (i - 1));
		}
	}
	rep(i,1,m) S[i] += g[i];
	for (int l = 1, r; l <= n; l = r + 1) {
		r = min(n, n / (n / l));
		ans += (2 * getPhi(n / l) - 1) * (S[get(r)] - S[get(l - 1)]);
	} cout << ans << '\n';
}



CF1163F

给你一个 \(n\) 个点,\(m\) 条边的无向图,每条边连接点 \(u, v\),并且有个长度 \(w\)

\(q\) 次询问,每次询问给你一对 \(t, x\),表示仅当前询问下,将 \(t\) 这条边的长度修改为 \(x\),请你输出当前 \(1\)\(n\) 的最短路长度。

\(1\le n, m, q\le 2\times 10^5\)

考虑分讨。记 \(\mathbb E\) 为最短路上边集。

  1. \((u,v) \not \in \mathbb E\)
    \(\min\{dis(1,u) + dis(v,n) + x, dis(1,v) + dis(u,n) + x, dis(1, n)\}\)
  2. \((u,v) \in \mathbb E\)
    如果 \(x\) 小于等于原边权 \(w\) 则取 \(dis(1,n) - w + x\)
    大于原边权情况的解是该问题的关键。

在这种情况下,我们需要讨论删除任意一条位于最短路上边的情况下新的最短路的取值。

假设新的最短路边集为 \(\mathbb E'\),则有 \(\mathbb E \cap \mathbb E'\) 一定能组成不超过两段路径,这些路径分别以 \(1\)\(n\) 作为端点。证明考虑最短路的连续性。

然后我们记 \(l_x\) 分别为 \(1\to x\) 的最短路上最后一条在 \(1\to n\) 最短路上的边位于路径上第几条,\(r_x\)\(n\to x\) 的最短路上相应结果。
可以通过最短路树上 dp 的方式求得所有点这两个值的答案。

然后我们就有每一条不在 \(1\to n\) 最短路上的边能更新的最短路上的边的集合了。我们发现可以在最短路上扫描线解决该问题。
不需要写线段树,只需要一个 multiset 维护当前能更新该边的集合最小值就行了。

然后对应查询 \(O(1)\)

code
#include <bits/stdc++.h>
#define int long long
using namespace std; using pii = pair<int,int>; using vi = vector<int>; using ll = long long; 
using ull = unsigned long long; using db = double; using ld = long double; using lll = __int128_t;
mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());
template <typename T> T rand(T l, T r) { return uniform_int_distribution<T>(l, r)(rnd); }
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
template <typename T1, typename T2> T1 min(T1 a, T2 b) { return a < b ? a : b; }
template <typename T1, typename T2> T1 max(T1 a, T2 b) { return a > b ? a : b; }
#define fsqrt(x) (__builtin_sqrtf(x))
#define fcbrt(x) (__builtin_cbrtf(x))
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define ufile(x) 
#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)
#define Aster(i, s) for (int i = head[s], v; i; i = e[i].next)
#define all(s) s.begin(), s.end()
#define eb emplace_back
#define pb pop_back
#define em emplace
const int N = 2e6 + 10;
const int inf = 0x3f3f3f3f3f3f3f3f;
const ll infll = 0x3f3f3f3f3f3f3f3f;
int n, m, q, t, t1, t2, a[N], id[N], l[N], r[N], p[N]; 
struct { int u, v, w; } ed[N];
multiset <ll> st;
vector<ll> g[N], h[N];

int head[N], mlc = 1;
struct ep {
    int to, next, w;
} e[N << 1];
void adde(int u, int v, int w) {
    e[++mlc] = {v, head[u], w}; head[u] = mlc;
    e[++mlc] = {u, head[v], w}; head[v] = mlc;
}

ll dis[2][N]; bool vis[N];
priority_queue <pair<ll,int> > que;
void dij(int st, int id) {
    rep(i,1,n) dis[id][i] = infll, vis[i] = 0;
    dis[id][st] = 0, que.emplace(0, st);
    while (que.size()) {
        int u = que.top().second; que.pop();
        if (vis[u]) continue; vis[u] = true;
        Aster(i, u) {
            v = e[i].to;
            if (dis[id][v] > dis[id][u] + e[i].w) {
                dis[id][v] = dis[id][u] + e[i].w;
                if (!vis[v]) que.emplace( - dis[id][v], v );
            }
        }
    }
}

signed main() {
    iot; cin >> n >> m >> q; memset(l, 0x3f, sizeof l);
    rep(i,1,m) cin >> ed[i].u >> ed[i].v >> ed[i].w, adde(ed[i].u, ed[i].v, ed[i].w);
    dij(1, 0), dij(n, 1);
    for (int i = 1, j, k; l[i] = t + 1, r[i] = t, i != n; i = k, p[j >> 1] = ++ t) 
        for (j = head[i]; dis[1][k = e[j].to] + e[j].w != dis[1][i]; j = e[j].next);
    rep(i,1,n) id[i] = i;
    sort(id + 1, id + 1 + n, [](int x, int y){ return dis[0][x] < dis[0][y]; });
    rep(i,1,n) for (int j = head[id[i]]; j; j = e[j].next) if (!p[j >> 1] and dis[0][e[j].to] == dis[0][id[i]] + e[j].w) l[e[j].to] = min(l[e[j].to], l[id[i]]);
    sort(id + 1, id + 1 + n, [](int x, int y){ return dis[1][x] < dis[1][y]; });
    rep(i,1,n) for (int j = head[id[i]]; j; j = e[j].next) if (!p[j >> 1] and dis[1][e[j].to] == dis[1][id[i]] + e[j].w) r[e[j].to] = max(r[e[j].to], r[id[i]]);
    rep(i,1,m) if (!p[i]) {
        if (l[ed[i].u] <= r[ed[i].v]) g[l[ed[i].u]].eb(dis[0][ed[i].u] + dis[1][ed[i].v] + ed[i].w), h[r[ed[i].v]].eb(dis[0][ed[i].u] + dis[1][ed[i].v] + ed[i].w);
        if (l[ed[i].v] <= r[ed[i].u]) g[l[ed[i].v]].eb(dis[0][ed[i].v] + dis[1][ed[i].u] + ed[i].w), h[r[ed[i].u]].eb(dis[0][ed[i].v] + dis[1][ed[i].u] + ed[i].w);
    } 
    rep(i,1,t) { 
        for (auto x : g[i]) st.insert(x);
        a[i] = (st.empty() ? infll : (*st.begin()));
        for (auto x : h[i]) st.erase(st.find(x));
    }
    rep(i,1,q) {
        cin >> t1 >> t2;
        if (p[t1]) cout << min(dis[0][n] - ed[t1].w + t2, a[p[t1]]) << "\n";
        else cout << min(min(dis[0][ed[t1].u] + dis[1][ed[t1].v], dis[1][ed[t1].u] + dis[0][ed[t1].v]) + t2, dis[0][n]) << '\n';
    }
}



归程

给定一张 \(n\) 个点,\(m\) 条边的无向图,边有两个权值 \((w, l)\)\(q\) 次询问,每次给定 \((u,x)\)。从 \(u\) 开始向 \(1\) 行走,走过的路分成两段,第一段需要保证走过的边 \(l > x\),第二段无要求。需要输出第二段的最短路程。

强制在线。

\(n\le 2\times 10^5, m,q\le 4\times 10^5\)

首先建出最大 Kruskal 重构树。然后我们发现满足第一段为 \(u\to v\) 的节点 \(v\)\(u\) 在重构树上的兄弟,他们的 LCA 处的值需要大于等于 \(x\)。这个最浅的 LCA 可以树上倍增求出。然后最短路程即为这个 LCA 子树上节点到 \(1\) 节点的最短路的最小值。
预处理最短路即可 \(O(\log n)\) 在线回答询问。
总时间复杂度 \(O(m\log n + q\log n)\)

然后不写代码了。

posted @ 2022-11-21 20:10  joke3579  阅读(51)  评论(0编辑  收藏  举报