1. 程式人生 > 實用技巧 >2020牛客暑期多校訓練營(第七場)

2020牛客暑期多校訓練營(第七場)

目錄

Contest Info


傳送門

Solved A B C D E F G H I J
6 / 10 - O O O - - - O O O
  • O 在比賽中通過
  • Ø 賽後通過
  • ! 嘗試了但是失敗了
  • - 沒有嘗試

Solutions


B. Mask Allocation

類似於歐幾里得演算法的想法去構造即可,這樣可取到最大值。

Code
// Author : heyuhhh
// Created Time : 2020/08/01 12:54:54
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
    int n, m;
    cin >> n >> m;
    vector<int> ans;
    while (n) {
        if (n < m) swap(n, m);
        for (int i = 1; i <= m; i++) {
            ans.push_back(m);
        }
        n -= m;
    }
    sort(all(ans));
    reverse(all(ans));
    cout << sz(ans) << '\n';
    for (auto it : ans) {
        cout << it << ' ';
    }
    cout << '\n';
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while(T--)
    run();
    return 0;
}

C. A National Pandemic

題意:
給定一顆無根樹,初始每個結點的權值為\(0\)
然後有以下三種操作:

  • 選定\(x\),然後每個點權值加上\(w-dist(x,y)\)
  • \(F(x)=min(F(x),0)\)
  • 詢問\(F(x)\)

思路:
稍加思考可以發現,二操作就是賣萌的,我們記錄一個delta值就行。
考慮如何處理一操作:\(w-dist(x,y)=w-(deep(x)+deep(y)-2lca(x,y))\)
那麼\(w,deep[x],deep[y]\)部分我們用全域性變數標記一下即可,主要是處理\(lca\)部分。
這一部分我們考慮差分來處理,即從每一個\(y\)

點出發跳到祖先能得到\(-2lca(x,y)\),所以只需要把\(x\)到祖先這條鏈權值都加上\(2\)即可。
用樹剖來處理的話時間複雜度為\(O(nlog^2n)\)
程式碼如下:

Code
// Author : heyuhhh
// Created Time : 2020/08/02 18:04:12
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
void err(int x) {cerr << x;}
void err(long long x) {cerr << x;}
void err(double x) {cerr << x;}
void err(char x) {cerr << '"' << x << '"';}
void err(const string &x) {cerr << '"' << x << '"';}
void _print() {cerr << "]\n";}
template<typename T, typename V>
  void err(const pair<T, V> &x) {cerr << '{'; err(x.first); cerr << ','; err(x.second); cerr << '}';}
template<typename T>
  void err(const T &x) {int f = 0; cerr << '{'; for (auto &i: x) cerr << (f++ ? "," : ""), err(i); cerr << "}";}
template <typename T, typename... V>
  void _print(T t, V... v) {err(t); if (sizeof...(v)) cerr << ", "; _print(v...);}
#ifdef Local
#define dbg(x...) cerr << "[" << #x << "] = ["; _print(x)
#else
#define dbg(x...)
#endif
//head
const int N = 1e5 + 5;

vector<int> G[N];
int n, m;
// 注意bson的初始化,其餘可以自動初始化
// 注意每個點實際值為dfn[x]
int sz[N], deep[N], bson[N], ff[N];
int top[N], dfn[N], T;
void dfs(int u, int fa) {
    deep[u] = deep[fa] + 1;
    sz[u] = 1;
    ff[u] = fa;
    int Max = -1;
    for (auto v : G[u]) {
        if (v != fa) {
            dfs(v, u);
            sz[u] += sz[v];
            if (sz[v] > Max) {
                Max = sz[v];
                bson[u] = v;
            }
        }
    }
}

void dfs(int u, int fa, int topf) {
    dfn[u] = ++T;
    top[u] = topf;
    if (bson[u] != 0) {
        dfs(bson[u], u, topf);
    }
    for (auto v : G[u]) {
        if (v != fa && v != bson[u]) {
            dfs(v, u, v);
        }
    }
}

ll sumv[N << 2], lz[N << 2];

//區間加
void tag(int o, int l, int r, ll v) {
    sumv[o] += 1ll * (r - l + 1) * v;
    lz[o] += v;
}

void push_up(int o) {
    sumv[o] = sumv[o << 1] + sumv[o << 1|1];
}

void push_down(int o, int l, int r) {
    if(lz[o] != 0) {
        int mid = (l + r) >> 1;
        tag(o << 1, l, mid, lz[o]);
        tag(o << 1|1, mid + 1, r, lz[o]);
        lz[o] = 0;   
    }
}

void build(int o, int l, int r) {
    lz[o] = 0;
    if(l == r) {
        sumv[o] = 0;
        return;
    }
    int mid = (l + r) >> 1;
    build(o << 1, l, mid), build(o << 1|1, mid + 1, r);
    push_up(o);
}

void update(int o, int l, int r, int L, int R, int v) {
    if(L <= l && r <= R) {
        tag(o, l, r, v);
        return;
    }   
    push_down(o, l, r);
    int mid = (l + r) >> 1;
    if(L <= mid) update(o << 1, l, mid, L, R, v);
    if(R > mid) update(o << 1|1, mid + 1, r, L, R, v);
    push_up(o);
}

ll query(int o, int l, int r, int L, int R) {
    if(L <= l && r <= R) {
        return sumv[o];
    }
    push_down(o, l, r);
    int mid = (l + r) >> 1;
    ll res = 0;
    if(L <= mid) res += query(o << 1, l, mid, L, R);
    if(R > mid) res += query(o << 1|1, mid + 1, r, L, R);
    return res;
}

ll add[N];
ll cnt, sumx;

ll query(int x) {
    ll res = 0;
    int t = x;
    while (x != 0) {
        res += query(1, 1, n, dfn[top[x]], dfn[x]);
        x = ff[top[x]];
    }
    return res + add[t] - 1ll * cnt * deep[t] + sumx;
}

void modify(int x) {
    while (x != 0) {
        update(1, 1, n, dfn[top[x]], dfn[x], 2);
        x = ff[top[x]];
    }
}



void run() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        G[i].clear();
        bson[i] = 0;
        add[i] = 0;
    }
    T = sumx = cnt = 0;
    for (int i = 1; i < n; i++) {
        int u, v;
        cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1, 0);
    dfs(1, 0, 1);
    build(1, 1, n);
    while (m--) {
        int op;
        cin >> op;
        if (op == 1) {
            int w, x;
            cin >> x >> w;
            ++cnt;
            sumx += w - deep[x];
            modify(x);
        } else if (op == 2) {
            int x;
            cin >> x;
            ll res = query(x);
            if (res > 0) {
                add[x] -= res;
            }
        } else {
            int x;
            cin >> x;
            ll res = query(x);
            cout << res << '\n';
        }
    }
}

int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while(T--)
    run();
    return 0;
}

也可以直接用點分樹來維護距離,這樣每次修改一個點過後,只會影響其餘每個點到它的logn條路徑,然後也是類似於差分的思想來計算答案。即當\(y\rightarrow f(y)\)時,我們需要統計來自另外子樹的貢獻,減去來自\(y\)這顆子樹的貢獻。所以修改值時要在每個父親結點打個正貢獻的標記,然後在他們父親處打個負貢獻標記即可。
直接copy別人的程式碼qaq。

Code
#include<bits/stdc++.h>
 
using namespace std;
 
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define LC k<<1
#define RC k<<1|1
#define tm tttadt
 
typedef long long LL;
const int N=510000;
const int M=1100000;
const LL mod=1e9+7;
 
 
int T,n,m;
LL delta[N],sumw;
struct point_tree
{
    vector<int> ch[N];
    vector<int> g[N];
    int f[N],sum,n,son[N],pp[N],nxt,root;
    int up[N],dis[20][N],dep[N];
    LL s1[20][N],s2[20][N];
    void getroot(int x,int fa)
    {
        son[x]=1,f[x]=0;
        for (int i=0;i<g[x].size();i++)
            if (!pp[g[x][i]]&&g[x][i]!=fa)
            {
                getroot(g[x][i],x);
                son[x]+=son[g[x][i]];
                f[x]=max(f[x],son[g[x][i]]);
            }
        f[x]=max(f[x],sum-son[x]);
        if (f[x]<f[nxt]) nxt=x;
    }
    void dfs(int x,int fa,int depth)
    {
        if (fa) dis[depth][x]=dis[depth][fa]+1;
        else dis[depth][x]=0;
        for (int i=0;i<g[x].size();i++)
            if (!pp[g[x][i]]&&g[x][i]!=fa) dfs(g[x][i],x,depth);
    }      
    void work(int x,int fa,int depth)
    {
        dep[x]=depth;
        dfs(x,0,depth);
        up[x]=fa;
        pp[x]=1;
        for (int i=0;i<g[x].size();i++)
            if (!pp[g[x][i]])
            {
                nxt=0,sum=son[g[x][i]];
                getroot(g[x][i],0);
                ch[x].pb(nxt);
                work(nxt,x,depth+1);
            }
    }
    void change(int x)
    {
        int cur=x,depth=dep[x],last=0;
        while (cur)
        {
            s1[depth][cur]+=dis[depth][x];
            s2[depth][cur]+=1;
            if (last) s1[depth][last]-=dis[depth][x],s2[depth][last]-=1;
            last=cur;
            cur=up[cur];
            depth--;
        }
    }
    LL ask(int x)
    {
        LL res=sumw-delta[x];
        int cur=x,depth=dep[x],last=0;
        while (cur)
        {
            res-=s1[depth][cur];
            res-=s2[depth][cur]*dis[depth][x];
            if (last) res-=s1[depth][last],res-=s2[depth][last]*dis[depth][x];
            last=cur;
            cur=up[cur];
            depth--;
        }
        return res;
    }
    void init()
    {
        for (int i=1;i<=n;i++)
            g[i].clear(),pp[i]=0;
        memset(s1,0,sizeof(s1));
        memset(s2,0,sizeof(s2));
        for (int i=1;i<n;i++)
        {
            int x,y;
            scanf("%d %d",&x,&y);
            g[x].pb(y);
            g[y].pb(x);
        }
        f[0]=sum=n;
        nxt=0;
        getroot(1,0);
        root=nxt;
        work(root,0,0);
    }
}A;
int main()
{
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d %d",&n,&m);
        sumw=0;
        A.n=n;
        A.init();
        for (int i=1;i<=n;i++)
            delta[i]=0;
        while (m--)
        {
            int op;
            scanf("%d",&op);
            if (op==1)
            {
                int x,w;
                scanf("%d %d",&x,&w);
                A.change(x);
                sumw+=w;
            }
            else if (op==2)
            {
                int x;
                scanf("%d",&x);
                LL val=A.ask(x);
                if (val>0) delta[x]+=val;
            }
            else
            {
                int x;
                scanf("%d",&x);
                LL val=A.ask(x);
                printf("%lld\n",val);
            }
        }
    }
    return 0;
}

D. Fake News

簽到。

Code
// Author : heyuhhh
// Created Time : 2020/08/01 12:06:53
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
    ll n;
    cin >> n;
    if (n == 1 || n == 24) {
        cout << "Fake news!" << '\n';
    } else {
        cout << "Nobody knows it better than me!" << '\n';
    }
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while (T--)
    run();
    return 0;
}

H. Dividing

數論分塊即可。

I. Valuable Forests

題意:
\(n,n\leq 5000\)個點生成一個森林,森林的貢獻為\(\sum (d(i))^2,d(i)\)代表每個結點的度數。
問所有情況的貢獻之和為多少。

思路:
觀察範圍首先可以明確複雜度應該是\(O(n^2)\)的。
因為這是樹上計數問題,那麼我們容易想到prufer定理。
首先有:\(n\)個點組成的有標號無根樹,其個數為\(n^{n-2}\)
然後\(n\)個點組成樹的度數貢獻之和為\(\displaystyle\sum_{i=1}^n\sum_{d=1}^{n-1}d^2{n-2\choose d-1}(n-1)^{n-2-d+1}\)。這一步也是根據prufer定理,因為定理內容則是將一顆樹與一個序列一一對應。
那麼剩下的便是一些基本的圖論上面dp的內容了,我們可以\(O(n^2)\)dp出\(f(n),F(n)\),分別表示\(n\)個點的森林有多少種情況,以及\(n\)個點的森林最終的答案為多少。這裡dp時有一個常見的思路,就是我們列舉\(i\)時,列舉\(i\)所在的環的大小,如果直接列舉環的大小可能會出問題。
另外還要注意算\(F\)時,要分別考慮兩部分的答案對另一部分貢獻的影響。
細節見程式碼:

Code
// Author : heyuhhh
// Created Time : 2020/08/05 20:10:05
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 5000 + 5;
int MOD;

int qpow(ll a, ll b) {
    ll res = 1;
    while(b) {
        if(b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;   
    }
    return res;   
}
int fac[N], inv[N];
void init() {
    fac[0] = 1;
    for(int i = 1; i < N; i++) fac[i] = 1ll * fac[i - 1] * i % MOD;
    inv[N - 1] = qpow(fac[N - 1], MOD - 2);
    for(int i = N - 2; i >= 0; i--) inv[i] = 1ll * inv[i + 1] * (i + 1) % MOD;
}
int C(int n, int m) {
    return 1ll * fac[n] * inv[m] % MOD * inv[n - m] % MOD;
}

int ans[N];
int f[N], A[N], a[N];

void run() {
    int T;
    cin >> T >> MOD;
    init();
    a[1] = 1;
    for (int i = 2; i < N; i++) {
        a[i] = qpow(i, i - 2);
    }
    
    f[0] = 1;
    for (int i = 1; i < N; i++) {
        for (int j = 0; j < i; j++) {
            f[i] = (f[i] + 1ll * C(i - 1, j) * f[i - j - 1] % MOD * a[j + 1] % MOD) % MOD;
        }
    }
    
    A[1] = 0;
    for (int i = 2; i < N; i++) {
        for (int d = 1; d < i; d++) {
            A[i] = (A[i] + 1ll * i * C(i - 2, d - 1) % MOD * d % MOD * d % MOD * qpow(i - 1, i - 2 - d + 1) % MOD) % MOD;
        }
    }
    

    ans[1] = 0;
    for (int i = 2; i < N; i++) {
        for (int j = 0; j < i; j++) {
            ans[i] = (ans[i] + 1ll * C(i - 1, j) * ((1ll * f[i - j - 1] * A[j + 1] % MOD 
                    + 1ll * ans[i - j - 1] * a[j + 1] % MOD) % MOD) % MOD) % MOD;
        }
    }
    while (T--) {
        int n;
        cin >> n;
        cout << ans[n] << '\n';
    }
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

J. Pointer Analysis

把題意理解清楚過後按照要求傳遞閉包即可,一開始只求了一次wa飛了,後來多求幾次閉包就A了qaq

Code
// Author : heyuhhh
// Created Time : 2020/08/01 14:33:39
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
void err(int x) {cerr << x;}
void err(long long x) {cerr << x;}
void err(double x) {cerr << x;}
void err(char x) {cerr << '"' << x << '"';}
void err(const string &x) {cerr << '"' << x << '"';}
void _print() {cerr << "]\n";}
template<typename T, typename V>
  void err(const pair<T, V> &x) {cerr << '{'; err(x.first); cerr << ','; err(x.second); cerr << '}';}
template<typename T>
  void err(const T &x) {int f = 0; cerr << '{'; for (auto &i: x) cerr << (f++ ? "," : ""), err(i); cerr << "}";}
template <typename T, typename... V>
  void _print(T t, V... v) {err(t); if (sizeof...(v)) cerr << ", "; _print(v...);}
#ifdef Local
#define dbg(x...) cerr << "[" << #x << "] = ["; _print(x)
#else
#define dbg(x...)
#endif
//head
const int N = 1000 + 5;
  
int mp[N][N];
  
void Floyd(int n) {
    for (int k = 0; k < n; k++) {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                mp[i][j] |= (mp[i][k] & mp[k][j]);
            }
        }
    }
}
  
void run() {
    int n;
    cin >> n;
    vector<string> el(n), er(n);
    for (int i = 0; i < n; i++) {
        string t;
        cin >> el[i] >> t >> er[i];
    }
    auto is_big = [&](char x) {
        return 'A' <= x && x <= 'Z';
    };
    auto trans_big = [&](char x) {
        return x - 'A';
    };
    auto trans_samll = [&](char x) {
        return x - 'a' + 26;
    };
    for (int i = 0; i < n; i++) {
        if (el[i].length() == 1 && er[i].length() == 1) {
            int u, v;
            if (is_big(el[i][0])) {
                u = trans_big(el[i][0]);
            } else {
                u = trans_samll(el[i][0]);
            }
            if (is_big(er[i][0])) {
                v = trans_big(er[i][0]);
            } else {
                v = trans_samll(er[i][0]);
            }
            // dbg(u, v);
            mp[u][v] = 1;
        }
    }
    int tot = 52;
    for (int i = 0; i < N; i++) {
        mp[i][i] = 1;
    }
    Floyd(tot);
 
    int cnt = 10;
    vector<vector<int>> val(52, vector<int>(26, -1));
 
    while (cnt--) {
        for (int i = 0; i < n; i++) {
            int l1 = el[i].length(), l2 = er[i].length();
            if (l1 == 1 && l2 == 1) continue;
            if (l2 == 1) {
                int u = trans_big(el[i][0]);
                int v = trans_big(er[i][0]);
                int t = trans_samll(el[i][2]) - 26;
                for (int j = 26; j < 52; j++) {
                    if (mp[u][j]) {
                        if (val[j][t] == -1) val[j][t] = tot++;
                        mp[val[j][t]][v] = 1;
                    }
                }
            }
        }
        Floyd(tot);
        for (int i = 0; i < n; i++) {
            int l1 = el[i].length(), l2 = er[i].length();
            if (l1 == 1 && l2 == 1) continue;
            if (l1 == 1) {
                int u = trans_big(el[i][0]);
                int v = trans_big(er[i][0]);
                int t = trans_samll(er[i][2]) - 26;
                for (int j = 26; j < 52; j++) {
                    if (mp[v][j]) {
                        // dbg(t, u, val[j][t]);
                        if (val[j][t] == -1) val[j][t] = tot++;
                        mp[u][val[j][t]] = 1;
                    }
                }
            }
        }
        Floyd(tot);       
    }
 
 
    vector<vector<char>> ans(26);
    for (int i = 0; i < 26; i++) {
        for (int j = 26; j < 52; j++) {
            if (mp[i][j]) {
                ans[i].push_back('a' + (j - 26)); 
            }
        }
        sort(all(ans[i]));
    }
    for (int i = 0; i < 26; i++) {
        cout << char('A' + i) << ": ";
        for (auto& it : ans[i]) {
            cout << it;
        }
        cout << '\n';
    }
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}s