https://atcoder.jp/contests/abc239/tasks
F - Construct Highway n 点, m边
问是否有方案 点i的连边数 = Di
所有点连成树
范围 n 2e5
2s
1024mb
我的思路
不成环
每个联通块->得到需要额外的度
贪心大的到小的连?
显然并查集先搞一搞
但不会实现如何后续的合并
题解 每次把节点大于1的和=1的连 !!!!!!!
直到剩余两个需求为1的块
证明 显然度需求1的肯定是和>1的连
那么假设 一个合法方案中1连的A, 任选一个度大于1的B
因为B度大于1,除了A…B路径外,肯定还连了一个块, 1-A-…-B-?, 那么 ?-A-…-B-1, 肯定也是一个合法方案
因此有合法方案, 任选的一个 >1的块, 依然有合法方案
代码 https://atcoder.jp/contests/abc239/submissions/34897350
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #include <bits/stdc++.h> using namespace std;#define rep(i,a,n) for (int i=a;i<(int)n;i++) int read () {int r;scanf ("%d" ,&r);return r;}int fa[200010 ];int d[200010 ];int getfa (int v) { return v == fa[v]?v:(fa[v] = getfa (fa[v]));}vector<int > arr[200010 ]; int main () { int n = read (); int m = read (); rep (i,1 ,n+1 ) d[i] = read (); iota (fa+1 ,fa+n+1 ,1 ); rep (i,0 ,m){ int u = read (); int v = read (); d[u]--; d[v]--; int fu = getfa (u); int fv = getfa (v); if (fu == fv){ printf ("-1\n" ); return 0 ; } fa[fu] = fv; } rep (i,1 ,n+1 ){ if (d[i] < 0 ){ printf ("-1\n" ); return 0 ; } int fi = getfa (i); rep (t,0 ,d[i]) arr[fi].push_back (i); } vector<int > e1; vector<int > g1; rep (i,1 ,n+1 ) if (i == getfa (i)) { if (!arr[i].size ()){ printf ("-1\n" ); return 0 ; } (arr[i].size () == 1 ?e1:g1).push_back (i); } vector<pair<int ,int >>ans; while (e1. size () && g1. size ()){ int pj = g1. back (); ans.push_back ({arr[e1. back ()].back (),arr[pj].back ()}); e1. pop_back (); arr[pj].pop_back (); if (arr[pj].size () == 1 ){ g1. pop_back (); e1. push_back (pj); } } if (g1. size () || 2 != (int )e1. size ()){ printf ("-1\n" ); return 0 ; } ans.push_back ({arr[e1[0 ]].back (),arr[e1[1 ]].back ()}); for (auto [u,v]:ans) printf ("%d %d\n" ,u,v); return 0 ; }
G - Builder Takahashi 连通 无向 n点,m边
没有直接的1-n的边
点可能 空/有障碍, 初始都是空
A要从1走到n
而T要选择一些点建立障碍(A不可经过障碍),让A无法走到n
不能选1和n建立障碍, 每个点建立的代价ci
求最小建立代价,和具体方案
范围 n 100
无重边自环
ci [1,1e9]
c1=cn=0
2s
1024mb
我的思路 有点网络流求最小割的味道
感觉 每个点拆成in/out, 然后 点内就是代价, 点间就是无限大代价?
这样就可以了?
问题是这样,是能算出代价, 但如何求一组割边?
题解 和我想的一样 也是 拆点做
显然割边流量跑满
但是是通过残量网络里, 从源可达的点选出来作为割断边
1->2->3, 这种 1->2,2->3都可以是割边时, 那么只有1可达
而对于 1->2->3->4, 1->3 这种,跑满1,2,3,4的来说, 2->3是不能成为割边的, 所以需要满足的是 a可达而b不可达,则a->b是割边
代码 https://atcoder.jp/contests/abc239/submissions/34897775
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <bits/stdc++.h> #include <atcoder/maxflow> using namespace std;typedef long long ll;#define rep(i,a,n) for (ll i=a;i<(ll)n;i++) ll read () {ll r;scanf ("%lld" ,&r);return r;}ll INF = 0x3f3f3f3f3f3f3f3f ; int main () { int n = read (); int m = read (); auto g = atcoder::mf_graph <ll>(n*2 ); rep (i,0 ,m){ int u = read ()-1 ; int v = read ()-1 ; g.add_edge (u*2 +1 ,v*2 ,INF); g.add_edge (v*2 +1 ,u*2 ,INF); } rep (i,0 ,n) g.add_edge (i*2 ,i*2 +1 ,read ()); int S = 0 *2 +1 ; int T = (n-1 )*2 ; ll c = g.flow (S,T); printf ("%lld\n" ,c); auto cut = g.min_cut (S); vector<int > ans; rep (i,1 ,n-1 ) if (cut[i*2 ] && !cut[i*2 +1 ]) ans.push_back (i+1 ); printf ("%d\n" ,(int )ans.size ()); for (auto u:ans) printf ("%d " ,u); return 0 ; }
Ex - Dice Product 2 snuke 有个骰子,上面1-n等概率
初始v=1
当v <= M时, 1~n等概率被选 v 乘上被选的值
求停止的期望次数
mod 1e9+7
范围 n,1e9
m,1e9
2s
1024mb
我的思路 惊了 atcoder竟然也有1e9+7
f(v) 表示从v开始,需要的期望次数
f(>m) = 0
f(v) = 1 + 1/n (f(v) + f(2v) + f(3v) + … + f(nv))
f(v) = n/(n-1) + 1/(n-1) (f(2v)+f(3v)+f(4v) +…+f(nv))
答案是f(1)
暴力似乎需要 O(m log(n)) 以上
但是注意到 (m/2,…,m] 都 = n/(n-1)
(m/3…m/2] 都 = n/(n-1) + 1/(n-1) * (n/(n-1))
(m/4…m/3] 都 = n/(n-1) + 1/(n-1) * (2n/(n-1))
似乎可以按照 (m/(i+1),m/i] 来分段
题解 emmm, 感觉概率论还是不会, 上面我这样也是对的吗?
g(x) = 当前值乘上 x 后 小于等于m 的期望次数
g(0) = 0
g(x) = 1+ 1/n(f(x/1)+f(x/2)+…+f(x/n))
g(x) = (n/(n-1)) (1+1/n(f(x/2)+f(x/3)+…+f(x/n)))
答案是 g(M)
啊 我看着好别扭啊
这里表达的应该是 g(x) = (v * x <=m )且(v * (x+1) > m) 的 v的期望次数
相当于我的f(1) = g(M), f(>M) = g(0)
v * x <= m, v * (x+1) > m
v * i * x <= m * i, v * i * (x+1) > m * i
v * i * (x/i) <= m , v * i * (x/i+1) > m (因为 a + 1 > b 则 a/b + 1 > 1
观察1
m/i 只有 O(sqrt(M)) 种, 易证(i=1..sqrt(M) 时 假设每个不一样, i=sqrt(M)..M 时 结果在[1..sqrt(M)]中, 因此 是sqrt M的量级
g(x) = (n/(n-1)) (1+ sum ( t(x,v) * g(v) )
其中v是 所有 m/i 的结果, t(x,v) 是它出现的次数
这样如果 子的计算完成了, 那么 g(x) 就可以再花sqrt(M)的完成计算了
观察2
m/i/j = m/(i * j)
这个也容易证明
可以想 i * j 的对整数分块, 后面的m/(ij) 就是问是在哪一块
而前面就是 i * j 的对整数分块,每块内再每i个一块, 后面的m/i 得到的就是 块号 * j + 块内的小块号, 再除j就只剩 块号了
这个还可以2推3 , m/i/j/k = m/(ijk)
因此 递归话只会有O(sqrt M) 个需要计算
观察3
f(M) 只需要 O(M^{3/4}) 即可算出
首先每个 一共需要算O(sqrt(M)) 个
f(x) 需要 O(sqrt(x))
sum (sqrt(M/[1..sqrt{M}]) ) (> sqrt M 部分) + sqrt{sqrt{M}} * sqrt{M} , (< sqrt M 部分)
int sqrt{M} 1/sqrt{x} dx , x= 1..sqrt{M}
= sqrt{M} 2(sqrt{sqrt{M}} - 1)
= M ^ {3/4}
代码 https://atcoder.jp/contests/abc239/submissions/34960041
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include <bits/stdc++.h> #include <atcoder/modint> using mint = atcoder::modint1000000007;using namespace std;typedef long long ll;#define rep(i,a,n) for (ll i=a;i<(ll)n;i++) ll read () {ll r;scanf ("%lld" ,&r);return r;}mint low[40000 ]; mint high[40000 ]; int main () { int N = read (); int M = read (); ll L = llround (sqrt (M)); ll K = M / (L + 1 ); rep (i,1 ,L+K+1 ){ int x = i <= L ? i : M / (L + K + 1 - i); mint sum = N; int l = 2 ; while (l <= min (N,x)){ int q = x / l; int r = min (N, x / q) + 1 ; mint cur = q <= L ? low[q] : high[M / q]; sum += cur * (r - l); l = r; } (i <= L ? low[i] : high[L + K + 1 - i]) = sum / (N - 1 ); } printf ("%d\n" ,(M <= L ? low[M] : high[1 ]).val ()); }
总结 F
又卡蓝题,不会数学,没有智慧
G
这个好像很久很久很久以前做USACO做过, 但好久都没求过割边集了, 现在拆点这些是会了, 这算又复习一下如何求割边集
Ex
概率论 每日自闭
学了收这样把乘和除法的转化(通过倒数? x => v/x), 没有悟到, 这样转化后, 似乎依赖的值不再像我的那样需要手工去判断 相同的是哪一段了, 鹅妹子嘤
数学完全不会
https://cplusplus.com/reference/cmath/llround/
参考 官方题解