題目連接:2018-2019 ACM-ICPC, Asia Xuzhou Regional Contestios
A. Rikka with Minimum Spanning Treesc++
題意:算法
給出一個隨機算法生成邊的信息,而後求最小生成樹的個數以及其權值的乘積。ide
題解:spa
這個隨機算法有點神奇...基本不會有重複的邊出現,因此其實只用求MST就好了。固然,其實經過樣例也能夠猜出來,樣例生成了1W條邊,但最後的answer就爲最小生成樹權值,因此能夠直接根據這個來猜一發,注意一下判斷是否連通就好了。code
代碼以下:blog
#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排序
題意:ci
樹上給出若干條簡單路徑,問有多少選k條路徑的方案,知足這些路徑的交至少有一個點。get
題解:
考慮求出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; }