【题解】The 2022 ICPC Asia Hangzhou Regional Programming Contest (第 47 届 ICPC 国际大学生程序设计竞赛亚洲区域赛(杭州))
D. Money Game
一开始有 \(n\) 个人围成一圈,第 \(i\) 个人手上有 \(a_i\) 的存款(实数),每一轮从第一个人开始,每个人把自己手上的一半存款给下一个人,问稳定时每个人手上存款有多少。
考虑两个人的情况:
所以:
类似的,三个人的情况可以知道稳态解为:
不难发现,\(n\) 个人的稳态解一定是:
相当于每个人给下一个人传了一单位。
设 \(w=\frac{1}{n+1}\sum_{i=1}^{n} a_i\),则答案为:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
typedef long long ll;
int n;
ll a[N];
int main() {
scanf("%d", &n);
ll sum = 0;
for(int i = 1 ; i <= n ; ++ i) {
scanf("%lld", &a[i]);
sum += a[i];
}
ll prod = n + 1;
printf("%.6lf ", (double) sum * 2 / prod);
double tmp = (double) sum / prod;
for(int i = 2 ; i <= n ; ++ i) {
printf("%.6lf ", tmp);
}
}
F. Da Mi Lao Shi Ai Kan De
签到题,为了防止被卡hash还写了个双hash。
#include <bits/stdc++.h>
using namespace std;
int n;
int len;
set<pair<int, int> > vis;
const int b1 = 149, b2 = 137;
const int m1 = 998244353, m2 = 1e9 + 7;
typedef long long ll;
char str[int(1e5) + 10];
int geths(int b, int m) {
int res = 0;
for(int i = 1 ; i <= len ; ++ i) {
res = ((ll) res * b + str[i]) % m;
}
return res;
}
int hasstr() {
int h1 = geths(b1, m1);
int h2 = geths(b2, m2);
if(vis.find(make_pair(h1, h2)) == vis.end()) {
vis.insert(make_pair(h1, h2));
return 0;
} else {
return 1;
}
}
int bie() {
for(int i = 1 ; i + 2 <= len ; ++ i) {
if(str[i] == 'b') {
if(str[i + 1] == 'i' && str[i + 2] == 'e') {
return 1;
}
}
}
return 0;
}
int main() {
scanf("%d", &n);
for(int i = 1 ; i <= n ; ++ i) {
int m; scanf("%d", &m);
int flag = 0;
while(m --) {
scanf("%s", str + 1);
len = strlen(str + 1);
if(bie()) {
if(!hasstr()) {
puts(str + 1);
flag = 1;
}
}
}
if(!flag) {
puts("Time to play Genshin Impact, Teacher Rice!");
}
}
}
G. Subgraph Isomorphism
给定一张无向连通图,问它的所有生成树是否同构。
只有当 \(G\) 退化为树或者基环树的时候才可能满足题意。
对于基环树的情况,合法的子树类型只有 (ab)(ab)(ab)(ab) 或者 (a)(a)(a)(a)。
树Hash是个玄学东西,可以参考:https://peehs-moorhsum.blog.uoj.ac/blog/7891
看起来只要 \(f\) 足够随机就行,把 \(h\) 数组后面随机加一些 Salt 也能过:
sort(h.begin(), h.end());
ull ret=sz[u]*magic2+dep[u]*magic3;
// 随机撒盐
srand(sz[u]);
random_shuffle(h.begin(), h.end());
int x = rand() % 10;
for(int i = 0 ; i <= x ; ++ i)
h.push_back(rand());
for(int i=0;i<(int)h.size();++i)
ret=ret*magic1+h[i];
J. Painting
有 \(n\) 条射线,第 \(i\) 条射线从 \((0,a_i)\) 往 \((W,b_i)\) 发射,当碰撞到已经有的射线时会停止。
依次绘制这 \(n\) 条射线,求每条射线停止的位置(用分数表示)。
若某条射线 \(a\) 停止在射线 \(b\) 的某一段,我们考虑将 \(b\) 看作 \(a\) 的父节点,这样所有节点就可以构成一棵树。
对于 \(x=0\) 上任意两个出射点 \(u,v\),路径 \(u \to \text{lca}(u,v) \to v\) 构成一个凸包。
不妨假设 \(y_u>y_v\),则 \(u \to \text{lca}\) 构成上凸壳,\(\text{lca} \to v\) 构成下凸壳。
设 \(d=\text{lca}(u,v)\),我们考虑在 \([u,d),[v,d)\) 这两条链上分别尝试求出来交点,若都没有交点,则交点在 \(d\) 所在直线上。
不要忘记考虑交点在之前的交点上的情况,比如说 getslope(u,a)==slp,此时 idx 的父节点应该是 u 的父节点。
关于精度问题,求交点时,应该用凸包上的线段所在的原始射线来求交点,而不应该用凸包上的两个交点求交点。
这很好理解,因为求交点会让位数翻倍,凸包上的点全都是 long long 范围,而原始射线端点是 int 范围。
也就是说注释的部分精度问题很大:
// nd[idx] = inj(nd[v], nd[fa[v][0]], Node(0, a), Node(W, b));
nd[idx] = inj(Node(0, :: a[fa[v][0]]), Node(W, :: b[fa[v][0]]), Node(0, a), Node(W, b));
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct FastIO {
static const int S = 2e7;
int wpos;
char wbuf[S];
FastIO() : wpos(0) {}
inline int xchar() {
static char buf[S];
static int len = 0, pos = 0;
if (pos == len)
pos = 0, len = fread(buf, 1, S, stdin);
if (pos == len) exit(0);
return buf[pos++];
}
inline int operator () () {
int c = xchar(), x = 0, flag = 0;
while (c <= 32) flag |= c == '-', c = xchar();
for (; '0' <= c && c <= '9'; c = xchar()) x = x * 10 + c - '0';
return flag ? -x : x;
}
inline ll operator ! () {
int c = xchar(); ll x = 0;
while (c <= 32) c = xchar();
for (; '0' <= c && c <= '9'; c = xchar()) x = x * 10 + c - '0';
return x;
}
inline void wchar(int x) {
if (wpos == S) fwrite(wbuf, 1, S, stdout), wpos = 0;
wbuf[wpos++] = x;
}
inline void operator () (ll x) {
if (x < 0) wchar('-'), x = -x;
char s[24];
int n = 0;
while (x || !n) s[n++] = '0' + x % 10, x /= 10;
while (n--) wchar(s[n]);
// wchar('\n');
}
inline void space(ll x) {
if (x < 0) wchar('-'), x = -x;
char s[24];
int n = 0;
while (x || !n) s[n++] = '0' + x % 10, x /= 10;
while (n--) wchar(s[n]);
wchar(' ');
}
inline void nextline() {
wchar('\n');
}
~FastIO() {
if (wpos) fwrite(wbuf, 1, wpos, stdout), wpos = 0;
}
} io;
const int N = 5e5 + 10;
set<pair<int, int> > segs;
int fa[N][25];
int n, W, cnt, dep[N];
ll gcd(ll a, ll b) {
if(!a && !b) return 1;
a = abs(a); b = abs(b);
return b ? gcd(b, a % b) : a;
}
#define __int128 ll
int getlca(int u, int v) {
if(dep[u] < dep[v]) swap(u, v);
for(int i = 20 ; i >= 0 ; -- i) {
if(dep[fa[u][i]] >= dep[v]) {
u = fa[u][i];
}
}
if(u == v) return u;
for(int i = 20 ; i >= 0 ; -- i) {
if(fa[u][i] != fa[v][i]) {
u = fa[u][i];
v = fa[v][i];
}
}
return fa[u][0];
}
int a[N], b[N];
struct Frac {
ll up, dn;
Frac(int x = 0) {
up = x;
dn = 1;
}
void abs() {
up = :: abs(up);
dn = :: abs(dn);
ll g = gcd(up, dn);
up /= g;
dn /= g;
}
};
Frac operator + (Frac a, Frac b) {
Frac res;
// ll g;
__int128 g;
res.up = (__int128) a.up * b.dn + (__int128) b.up * a.dn;
res.dn = (__int128) a.dn * b.dn;
g = gcd(res.up, res.dn);
res.up /= g;
res.dn /= g;
return res;
}
Frac operator - (Frac a, Frac b) {
b.up *= -1;
return a + b;
}
Frac operator * (Frac a, Frac b) {
Frac res;
// ll g;
__int128 g;
res.up = (__int128) a.up * b.up;
res.dn = (__int128) a.dn * b.dn;
g = gcd(res.up, res.dn);
res.up /= g;
res.dn /= g;
return res;
}
Frac operator / (Frac a, Frac b) {
Frac res;
// ll g;
__int128 g;
res.up = (__int128) a.up * b.dn;
res.dn = (__int128) a.dn * b.up;
g = gcd(res.up, res.dn);
res.up /= g;
res.dn /= g;
return res;
}
Frac operator - (Frac a) {
a.up *= -1;
return a;
}
bool operator <= (Frac a, Frac b) {
if(a.dn < 0) a.up *= -1, a.dn *= -1;
if(b.dn < 0) b.up *= -1, b.dn *= -1;
return (__int128) a.up * b.dn <= (__int128) b.up * a.dn;
}
bool operator >= (Frac a, Frac b) {
if(a.dn < 0) a.up *= -1, a.dn *= -1;
if(b.dn < 0) b.up *= -1, b.dn *= -1;
return (__int128) a.up * b.dn >= (__int128) b.up * a.dn;
}
bool operator == (Frac a, Frac b) {
return (__int128) a.up * b.dn == (__int128) b.up * a.dn;
}
struct Node {
Frac x, y;
Node(int ix = 0, int iy = 0) {
x = Frac(ix);
y = Frac(iy);
}
void abs() {
x.abs();
y.abs();
}
} nd[N];
Node operator * (Node a, Frac b) {
a.x = a.x * b;
a.y = a.y * b;
return a;
}
Node operator / (Node a, Frac b) {
a.x = a.x / b;
a.y = a.y / b;
return a;
}
Node operator + (Node a, Node b) {
a.x = a.x + b.x;
a.y = a.y + b.y;
return a;
}
#define cross(p1,p2,p3) ((p2.x-p1.x)*(p3.y-p1.y)-(p3.x-p1.x)*(p2.y-p1.y))
Node inj(Node p1, Node p2, Node q1, Node q2) {
Frac a1 = cross(q1,q2,p1);
Frac a2 = -cross(q1,q2,p2);
return (p1 * a2 + p2 * a1) / (a1 + a2);
}
Frac getslope(int idx, int a) {
Node p = nd[idx];
Node q = Node(0, a);
Frac res = (p.y - q.y) / (p.x - q.x);
return res;
}
int getfa(int idx, int a, int b) {
if(segs.empty()) {
nd[idx] = Node(W, b);
return n + 1;
}
int u, v;
if(segs.begin() -> first > a) {
u = segs.begin() -> second;
v = n + 1;
} else if((-- segs.end()) -> first < a) {
u = n + 1;
v = (-- segs.end()) -> second;
} else {
auto it = segs.lower_bound(make_pair(a, 0));
u = it -> second;
v = (-- it) -> second;
}
int d = getlca(u, v);
Frac slp = Frac(b-a) / Frac(W);
if(u != d) {
if(getslope(u, a) <= slp) {
nd[idx] = inj(Node(0, :: a[u]), Node(W, :: b[u]), Node(0, a), Node(W, b));
return getslope(u, a) == slp ? fa[u][0] : u;
} else {
for(int i = 20 ; i >= 0 ; -- i) {
if(dep[fa[u][i]] <= dep[d]) continue;
if(getslope(fa[u][i], a) >= slp) {
u = fa[u][i];
}
}
if(getslope(u, a) >= slp) {
if(getslope(u, a) == slp) {
nd[idx] = nd[u];
return fa[u][0];
} else if(fa[u][0] != d && getslope(fa[u][0], a) <= slp) {
// nd[idx] = inj(nd[u], nd[fa[u][0]], Node(0, a), Node(W, b));
nd[idx] = inj(Node(0, :: a[fa[u][0]]), Node(W, :: b[fa[u][0]]), Node(0, a), Node(W, b));
return fa[u][0];
}
}
}
}
if(v != d) {
if(getslope(v, a) >= slp) {
nd[idx] = inj(Node(0, :: a[v]), Node(W, :: b[v]), Node(0, a), Node(W, b));
return getslope(v, a) == slp ? fa[v][0] : v;
} else {
for(int i = 20 ; i >= 0 ; -- i) {
if(dep[fa[v][i]] <= dep[d]) continue;
if(getslope(fa[v][i], a) <= slp) {
v = fa[v][i];
}
}
if(getslope(v, a) <= slp) {
if(getslope(v, a) == slp) {
nd[idx] = nd[v];
return fa[v][0];
} else if(fa[v][0] != d && getslope(fa[v][0], a) >= slp) {
// nd[idx] = inj(nd[v], nd[fa[v][0]], Node(0, a), Node(W, b));
nd[idx] = inj(Node(0, :: a[fa[v][0]]), Node(W, :: b[fa[v][0]]), Node(0, a), Node(W, b));
return fa[v][0];
}
}
}
}
if(d == n + 1) {
nd[idx] = Node(W, b);
return n + 1;
} else {
nd[idx] = inj(Node(0, :: a[d]), Node(W, :: b[d]), Node(0, a), Node(W, b));
return d;
}
}
int main() {
n = io(), W = io();
dep[n + 1] = 1;
for(int i = 0 ; i <= 20 ; ++ i) {
fa[n + 1][i] = n + 1;
}
for(int i = 1 ; i <= n ; ++ i) {
int a, b, c;
a = io(), b = io(), c = io();
:: a[i] = a, :: b[i] = b;
int f = getfa(i, a, b);
if(c) {
segs.insert(make_pair(a, i));
fa[i][0] = f;
dep[i] = dep[f] + 1;
for(int j = 1 ; j <= 20 ; ++ j) {
fa[i][j] = fa[fa[i][j - 1]][j - 1];
}
}
nd[i].abs();
io.wchar('(');
io(nd[i].x.up);
io.wchar('/');
io(nd[i].x.dn);
io.wchar(',');
io(nd[i].y.up);
io.wchar('/');
io(nd[i].y.dn);
io.wchar(')');
io.wchar('\n');
}
}
M. Please Save Pigeland
给定一棵 \(n\) 个点的树,上面有 \(k\) 个关键点,对于每一个点 \(u\),求:
- \(u\) 到所有关键点的距离之和;
- \(u\) 到所有关键点的距离的 \(\gcd\)。
首先 \(u=1\) 时,这两个信息都很容易求到,考虑换根的同时维护这两个信息。
将关键点的 dfs 序离散化,当转移 \(u \to v\) 时(边权为 \(w\)),相当于把在 \(v\) 子树内的关键点距离都减去 \(w\),然后把在 \(V\) 子树外的关键点距离都加上 \(w\),即在 dfs 序上是维护区间加,然后查询全区间的和。
对于 \(\gcd\) 信息,由更相减损术可知:
因此只需要维护差分数组的 \(\gcd\),然后求 \([2,n]\) 的 \(\gcd\) 与原数组 \([1,1]\) 的 \(\gcd\) 即可。
需要注意一些边界情况:
- 若 \(k=1\),则答案为 \(0\);
- 若某一个点的子树中无关键点,则转移到这个点时应该将全区间的距离都加上 \(w\)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct FastIO {
static const int S = 2e7;
int wpos;
char wbuf[S];
FastIO() : wpos(0) {}
inline int xchar() {
static char buf[S];
static int len = 0, pos = 0;
if (pos == len)
pos = 0, len = fread(buf, 1, S, stdin);
if (pos == len) exit(0);
return buf[pos++];
}
inline int operator () () {
int c = xchar(), x = 0, flag = 0;
while (c <= 32) flag |= c == '-', c = xchar();
for (; '0' <= c && c <= '9'; c = xchar()) x = x * 10 + c - '0';
return flag ? -x : x;
}
inline ll operator ! () {
int c = xchar(); ll x = 0;
while (c <= 32) c = xchar();
for (; '0' <= c && c <= '9'; c = xchar()) x = x * 10 + c - '0';
return x;
}
inline void wchar(int x) {
if (wpos == S) fwrite(wbuf, 1, S, stdout), wpos = 0;
wbuf[wpos++] = x;
}
inline void operator () (ll x) {
if (x < 0) wchar('-'), x = -x;
char s[24];
int n = 0;
while (x || !n) s[n++] = '0' + x % 10, x /= 10;
while (n--) wchar(s[n]);
wchar('\n');
}
inline void space(ll x) {
if (x < 0) wchar('-'), x = -x;
char s[24];
int n = 0;
while (x || !n) s[n++] = '0' + x % 10, x /= 10;
while (n--) wchar(s[n]);
wchar(' ');
}
inline void nextline() {
wchar('\n');
}
~FastIO() {
if (wpos) fwrite(wbuf, 1, wpos, stdout), wpos = 0;
}
} io;
const int N = 1e6 + 10;
int n, k;
int head[N], rest[N * 2], to[N * 2], w[N * 2], tot;
void add(int u, int v, int w) {
to[++ tot] = v;
:: w[tot] = w;
rest[tot] = head[u];
head[u] = tot;
}
int imp[N];
int clk, dfnl[N], dfnr[N], rev[N], tid[N];
ll dep[N];
int sz[N];
ll bgcd(ll a, ll b) {
return b ? bgcd(b, a % b) : a;
}
#define lc (id << 1)
#define rc (id << 1 | 1)
namespace seg_diff {
ll gcd[N * 8];
void modify(int id, int l, int r, int pos, ll val) {
if(pos < l || pos > r) return ;
int mid = (l + r) >> 1;
if(l == r) {
gcd[id] += val;
return ;
} else if(pos <= mid) {
modify(lc, l, mid, pos, val);
} else {
modify(rc, mid + 1, r, pos, val);
}
gcd[id] = bgcd(gcd[lc], gcd[rc]);
}
ll query(int id, int l, int r, int ql, int qr) {
int mid = (l + r) >> 1;
if(ql <= l && r <= qr) {
return gcd[id];
} else if(qr < l || ql > r) {
return 0;
} else {
return bgcd(query(lc, l, mid, ql, qr), query(rc, mid + 1, r, ql, qr));
}
}
};
namespace seg_norm {
ll tag[N * 8], sum[N * 8];
void modify(int id, int l, int r, int ql, int qr, ll val) {
if(ql <= l && r <= qr) {
tag[id] += val;
} else if(qr < l || ql > r) {
return ;
} else {
int mid = (l + r) >> 1;
sum[id] += val * (min(r, qr) - max(l, ql) + 1);
modify(lc, l, mid, ql, qr, val);
modify(rc, mid + 1, r, ql, qr, val);
}
}
ll query(int id, int l, int r, int ql, int qr) {
if(ql <= l && r <= qr) {
return sum[id] + tag[id] * (r - l + 1);
} else if(qr < l || ql > r) {
return 0;
} else {
int mid = (l + r) >> 1;
ll t = tag[id] * (min(r, qr) - max(l, ql) + 1);
return t + query(lc, l, mid, ql, qr) + query(rc, mid + 1, r, ql, qr);
}
}
};
ll ans_dis[N], ans_gcd[N];
int rev_l[N], rev_r[N];
signed main() {
n = io(), k = io();
for(int i = 1, x ; i <= k ; ++ i) {
imp[io()] = 1;
}
for(int i = 1 ; i < n ; ++ i) {
int u = io(), v = io(), w = io();
add(u, v, w);
add(v, u, w);
}
if(k == 1) {
puts("0");
return 0;
}
if(1) {
function<void(int, int)> dfs = [&] (int u, int fa) {
++ clk;
dfnl[u] = clk;
tid[clk] = u;
sz[u] = imp[u];
for(int i = head[u] ; i ; i = rest[i]) {
int v = to[i];
if(v == fa) continue;
dep[v] = dep[u] + w[i];
dfs(v, u);
sz[u] += sz[v];
}
dfnr[u] = clk;
};
dfs(1, 0);
}
vector<int> lsh;
for(int i = 1 ; i <= n ; ++ i) {
if(imp[i]) {
lsh.push_back(dfnl[i]);
}
}
sort(lsh.begin(), lsh.end());
lsh.erase(unique(lsh.begin(), lsh.end()), lsh.end());
for(int i = 1 ; i <= n ; ++ i) {
rev[i] = lower_bound(lsh.begin(), lsh.end(), dfnl[i]) - lsh.begin() + 1;
rev_l[i] = rev[i];
rev_r[i] = upper_bound(lsh.begin(), lsh.end(), dfnr[i]) - lsh.begin();
rev_r[i] = max(rev_r[i], rev_l[i]);
}
int len = lsh.size();
vector<int> arr;
for(int i = 0 ; i < len ; ++ i) {
arr.push_back(tid[lsh[i]]);
}
for(int i = 0 ; i < len ; ++ i) {
seg_norm :: modify(1, 1, len, i + 1, i + 1, dep[arr[i]]);
seg_diff :: modify(1, 1, len, i + 1, dep[arr[i]] - (i==0?0:dep[arr[i-1]]));
}
if(1) {
function<void(int, int)> dfs = [&] (int u, int fa) {
ans_dis[u] = seg_norm :: query(1, 1, len, 1, len);
ans_gcd[u] = bgcd(seg_norm :: query(1, 1, len, 1, 1),
seg_diff :: query(1, 1, len, 2, len));
for(int i = head[u] ; i ; i = rest[i]) {
int v = to[i];
if(v == fa) continue;
if(sz[v] == 0) {
seg_norm :: modify(1, 1, len, 1, len, w[i]);
seg_diff :: modify(1, 1, len, 1, w[i]);
dfs(v, u);
seg_norm :: modify(1, 1, len, 1, len, -w[i]);
seg_diff :: modify(1, 1, len, 1, -w[i]);
} else {
seg_norm :: modify(1, 1, len, rev_l[v], rev_r[v], -w[i]);
seg_norm :: modify(1, 1, len, 1, rev_l[v]-1, w[i]);
seg_norm :: modify(1, 1, len, rev_r[v]+1, len, w[i]);
seg_diff :: modify(1, 1, len, 1, w[i]);
seg_diff :: modify(1, 1, len, rev_l[v], -2*w[i]);
seg_diff :: modify(1, 1, len, rev_r[v]+1, 2*w[i]);
dfs(v, u);
seg_norm :: modify(1, 1, len, rev_l[v], rev_r[v], w[i]);
seg_norm :: modify(1, 1, len, 1, rev_l[v]-1, -w[i]);
seg_norm :: modify(1, 1, len, rev_r[v]+1, len, -w[i]);
seg_diff :: modify(1, 1, len, 1, -w[i]);
seg_diff :: modify(1, 1, len, rev_l[v], 2*w[i]);
seg_diff :: modify(1, 1, len, rev_r[v]+1, -2*w[i]);
}
}
};
dfs(1, 0);
}
ll ans = ans_dis[1] / abs(ans_gcd[1]);
for(int i = 1 ; i <= n ; ++ i) {
ans = min(ans, ans_dis[i] / abs(ans_gcd[i]));
}
printf("%lld\n", ans * 2);
}

浙公网安备 33010602011771号