題目鏈接:2018-2019 ACM-ICPC, Asia Xuzhou Regional Contest
A. Rikka with Minimum Spanning Trees
題意:
給出一個隨機算法生成邊的信息,然后求最小生成樹的個數以及其權值的乘積。
題解:
這個隨機算法有點神奇...基本不會有重復的邊出現,所以其實只用求MST就行了。當然,其實通過樣例也可以猜出來,樣例生成了1W條邊,但最后的answer就為最小生成樹權值,所以可以直接根據這個來猜一發,注意一下判斷是否連通就行了。
代碼如下:

#include <bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN = 1e5+5; const int MAXM = 3e5+5; const int MOD = 1e9+7; const double eps = 1e-7; typedef unsigned long long ull; #define rep(i,a,b) for(int i = (a);i<=(b);i++) struct Edge{ int u,v; ull w; bool operator <(const Edge &ds)const{ return w<ds.w; } }e[MAXM]; int n,m,fa[MAXN]; int t,tot; //double G[MAXN][MAXN]; ull k1,k2; ull ans=0; ull xorShift128Plus(){ ull k3=k1,k4=k2; k1=k4; k3 ^= k3<<23; k2 = k3 ^ k4 ^ (k3 >>17) ^(k4 >>26); return k2 +k4; } void gen(){ cin >>n >>m >>k1 >>k2; int u,v; ull w; tot = ans = 0; rep(i,1,m){ u = xorShift128Plus()%n+1; v = xorShift128Plus()%n+1; w = xorShift128Plus(); if(u == v) continue ; //cout <<u<<' ' <<v<<' ' <<w <<'\n'; e[++tot] = {u,v,w}; } } int find(int x){ return x==fa[x]?x:fa[x] = find(fa[x]); } bool kruskal(){ int ss=0; sort(e+1,e+1+tot); rep(i,1,n)fa[i] =i; rep(i,1,tot){ int a = find(e[i].u),b =find(e[i].v); if(a!=b){ fa[a] =b; ans = (ans +e[i].w%MOD)%MOD; ss++; } } return ss==n-1; } int main() { ios::sync_with_stdio(false);cin.tie(0) ; cin >>t; while(t--){ gen(); if(!kruskal()){ cout << 0<<'\n'; }else{ cout << ans <<'\n'; } } return 0 ; }
G. Rikka with Intersections of Paths
題意:
樹上給出若干條簡單路徑,問有多少選k條路徑的方案,滿足這些路徑的交至少有一個點。
題解:
考慮求出LCA,因為樹上簡單路徑至少存在一個交點為至少一條路徑的兩端點的LCA,同時可以利用樹上差分求出有多少條路徑經過當前點。
之后計算貢獻就行了,但是這里直接計算C(cnt, k)會有重復計算的,我們這里可以考慮剛才關於LCA的性質,求出每個點為多少條路徑端點的LCA,個數記為pi,那么最后答案就是C(cnt,k) - C(cnt - pi,k),此時選出的路徑中,至少有一條路徑的兩端點的LCA為當前點,此時就不會重復計算了。因為一條路徑的LCA只有一個,我們只會計算經過LCA時的貢獻。
代碼如下:

#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 3e5 + 5, MOD = 1e9 + 7; int T, n, m, k; struct Edge{ int u,v,next; }e[N << 1]; int head[N],tot; void adde(int u, int v) { e[tot].v = v; e[tot].next = head[u] ; head[u] = tot++ ; } int deep[N], st[N][22], sum[N], cnt[N]; ll fac[N], inv[N]; void dfs(int u, int d, int fa) { st[u][0] = fa; deep[u] = d; for(int i = head[u]; i != -1; i = e[i].next) { int v = e[i].v; if(v == fa) continue ; dfs(v, d + 1, u); } } ll qp(ll a, ll b) { ll ans = 1; while(b) { if(b & 1) ans = ans * a % MOD; a = a * a % MOD; b >>= 1; } return ans ; } void init() { for(int i = 1 ; i <= 20 ; i++) for(int j = 1 ; j <= n ; j++) st[j][i] = st[st[j][i - 1]][i - 1]; } int LCA(int x, int y) { if(deep[y] > deep[x]) swap(x , y) ; while(deep[x] != deep[y]) { int d = deep[x] - deep[y] ; for(int i = 0 ; i <= 20 ; i++) { if(d >> i & 1) x = st[x][i] ; } } if(x == y) return x ; for(int i = 20 ; i >= 0; i--) { if(st[x][i] != st[y][i]) { x = st[x][i] ; y = st[y][i] ; } } return st[x][0] ; } void pre(int u, int fa) { for(int i = head[u] ; i != -1; i = e[i].next) { int v = e[i].v; if(v == fa) continue ; pre(v , u); sum[u] += sum[v] ; } } ll C(ll a ,ll b) { if(b == 0 || a < b) return 0 ; return fac[a] * inv[b] % MOD * inv[a - b] % MOD; } ll calc(ll x) { ll ans = C(sum[x], k); ans = ((ans - C(sum[x] - cnt[x], k) % MOD ) % MOD + MOD ) % MOD; return ans ; } int main() { ios::sync_with_stdio(false);cin.tie(0); fac[0] = 1; inv[0] = 1; for(int i = 1; i < N; i++) { fac[i] = fac[i - 1] * i % MOD; inv[i] = qp(fac[i] , MOD - 2) ; } cin >> T; while(T--) { memset(head,-1,sizeof(head)); tot = 0; cin >> n >> m >> k ; for(int i = 1 ; i < n ; i++) { int u, v; cin >> u >> v; adde(u, v);adde(v, u); } dfs(1, 0, 0); init() ; for(int i = 1; i <= m ;i++) { int u, v; cin >> u >> v ; int x = LCA(u , v); sum[u]++;sum[v]++; sum[x]--;sum[st[x][0]]--; cnt[x]++; } pre(1, 0) ; ll ans = 0; for(int i = 1; i <= n; i++) { ans = (ans + calc(i)) % MOD ; } cout << ans << '\n' ; for(int i = 1; i <= n ; i++) deep[i] = sum[i] = cnt[i] = 0; } return 0 ; }
H. Rikka with A Long Colour Palette
題意:
給出n個區間,k個顏色,現在給區間染色,問怎么染色能使得覆蓋有所有顏色的區間長度最大。
題解:
染色過程考慮貪心。我們先按照區間左端點排序,然后依次塗顏色,顏色塗完了之后該怎么塗呢?假設當前左端點為L,並且在之前的區間中,有Ri,Rj,Rk滿足Ri < L < Rj < Rk,那么此時我們塗Ri的顏色肯定是最優的;如果不存在一個Ri,滿足L < Ri,這時我們也只需要塗處於最左端的Ri,因為這樣可以盡可能地增多所有顏色覆蓋的區間長度。
最后就考慮如果計算答案了,將所有點排序后記錄左端點為1,右端點為-1,累計前綴和,當和大於等於k時更新答案即可。
代碼如下:

#include <bits/stdc++.h> #define mp make_pair using namespace std; typedef long long ll; typedef pair<int,int> pii; const int N = 2e5 + 5; int T; int n, k, cnt; pair<pii,int> a[N << 1]; int col[N], has[N], answer[N]; struct Node { int l, r, id ; bool operator < (const Node &A) const { if(l == A.l) return r < A.r; return l < A.l; } }p[N]; int main() { ios::sync_with_stdio(false);cin.tie(0); cin >> T; while (T--) { cin >> n >> k; cnt = 0; for(int i = 1 ; i <= n ; i++) { int l, r; cin >> l >> r; p[i] = Node{l,r,i}; } if(n < k) { cout << 0 << '\n' ; for(int i = 1; i < n; i++) cout << 1 << ' ' ; cout << 1 << '\n' ; continue ; } sort(p + 1 , p + n + 1); priority_queue <pii> q; for(int i = 1 ; i <= k ; i++) { q.push(mp(0, i)) ; } for(int i = 1 ; i <= n ; i++) { int now = q.top().second;q.pop(); answer[p[i].id] = col[i] = now; q.push(mp(-p[i].r,now)) ; } for(int i = 1; i <= n ;i++) { a[++cnt] = mp(mp(p[i].l,col[i]),1) ; a[++cnt] = mp(mp(p[i].r,col[i]),-1) ; } sort(a + 1, a + cnt + 1) ; int ans = 0, cur = 0; for(int i = 1 ; i <= cnt ; i++) { if(has[a[i].first.second]) cur--; has[a[i].first.second] += a[i].second ; if(has[a[i].first.second]) cur++; if(cur >= k ) ans += a[i + 1].first.first - a[i].first.first; } cout << ans << '\n' ; for(int i = 1; i < n ; i++) cout << answer[i] << ' '; cout << answer[n] << '\n' ; for(int i = 1; i <= k ; i++) has[i] = 0; } return 0; }
I. Rikka with Sorting Networks
題意:
給出n個數,有k個排序器,每個排序器會使得au < av,即讓他們的位置相對有序,問有多少個排列,最后通過這k個排序器后,形成的序列最長上升子序列至少為n - 1。
題解:
滿足條件的最長上升子序列個數為(n - 1) ^ 2 + 1個,由於數據范圍很小,我們直接構造出來爆搜求解即可。
代碼如下:

#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 55; int a[N], b[N] ; int n, k, T, mod; int from[N], to[N] ; int ans; void dfs(int i, int f) { if(i == 0) { ans += f; if(ans >= mod) ans -= mod; return ; } if(a[from[i]] < a[to[i]]) { dfs(i - 1, f) ; swap(a[from[i]], a[to[i]]) ; dfs(i - 1, f) ; swap(a[from[i]], a[to[i]]) ; } } int main() { ios::sync_with_stdio(false);cin.tie(0); cin >> T; while(T--) { cin >> n >> k >> mod; for(int i = 1 ; i <= k ; ++i) cin >> from[i] >> to[i] ; for(int i = 1 ; i <= n ; ++i) a[i] = i ; ans = 0; dfs(k, 1) ; for(int i = 1 ; i < n ; ++i) { swap(a[i], a[i + 1]); dfs(k, -1); swap(a[i], a[i + 1]); } for(int take = 1 ; take <= n ; ++take) { for(int i = 1 ; i <= n ; ++i) { if(i == take) continue ; int cur = 0; for(int j = 1 ; j <= n ; ++j) { if(cur == take - 1) cur++; if(i == j) a[j] = take ; else a[j] = ++cur; } dfs(k , 1); } } cout << ans << '\n'; } return 0; }