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

也可以直接用点分树来维护距离,具体来说就是\(x,y\)之间的距离就是两个点在点分树中到他们lca的距离之和,因为树高为\(logn\),所以每次修改一个点过后,只会影响其余每个点到它的logn种路径。然后也是类似于差分的思想来计算答案。即当\(y\rightarrow f(y)\)时,我们需要统计来自另外子树的贡献,减去来自\(y\)这颗子树的贡献。所以修改值时要在每个父亲结点打个正贡献的标记,然后在他们父亲处打个负贡献标记即可。
这样时间复杂度为\(O(nlogn)\)
直接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
posted @ 2020-08-08 09:53  heyuhhh  阅读(162)  评论(0编辑  收藏  举报