2018-2019 ACM-ICPC 徐州區域賽 部分題解

題目連接: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 ;
}
View Code

 

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 ;
}
View Code

 

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;
}
View Code

 

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;
}
View Code
相關文章
相關標籤/搜索