• 2020 Multi-University Training Contest 6


    Contest Info


    传送门

    Solved A B C D E F G H I J K
    11 / 13 O O Ø - O O Ø Ø O O -
    • O 在比赛中通过
    • Ø 赛后通过
    • ! 尝试了但是失败了
    • - 没有尝试

    Solutions


    A. Road To The 3rd Building

    考虑每个长度对答案的贡献,并且将和式转化为前缀和的形式。那么容易发现对于每个长度(l),只有前面(0...l-1)是负贡献,后面(l+1...n)是正贡献。所以再统计前缀的前缀就可求出答案。
    注意(l)的答案是具有对称性的。稍微手模一下找下规律即可。

    Code
    // Author : heyuhhh
    // Created Time : 2020/08/06 13:05:06
    #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 = 2e5 + 5, MOD = 1e9 + 7;
    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 n;
    int a[N], ssum[N], sum[N];
    
    void run() {
        cin >> n;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            ssum[i] = (ssum[i - 1] + a[i]) % MOD;
            sum[i] = (sum[i - 1] + ssum[i]) % MOD;
        }
        int ans = 0;
        int p = (n + 1) / 2;
        for (int l = 1; l <= n; l++) {
            int res = 0;
            if (l <= p) {
                res = ((ll)sum[n] - sum[n - l] - sum[l - 1] + MOD + MOD) % MOD;
            } else {
                int qq = n - l + 1;
                res = ((ll)sum[n] - sum[n - qq] - sum[qq - 1] + MOD + MOD) % MOD;
            }
    
            res = 1ll * res * qpow(l, MOD - 2) % MOD;
            ans = (ans + res) % MOD;
        }
        ans = ans * 2 % MOD;
        int fm = qpow(1ll * n * (n + 1) % MOD, MOD - 2);
        ans = 1ll * ans * fm % MOD;
        cout << ans << '
    ';
    }
    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;
    }
    

    B. Little Rabbit's Equation

    貌似模拟一下就行。

    C. Borrow

    题意:
    给出三个数,每次最大的那个数会减一,然后另外两个数随机选一个加一。
    求三个数都相等的期望操作次数。

    思路:
    首先令(k=frac{x+y+z}{3})
    一开始思路是(f_x)表示最大值从(x ightarrow k)的期望次数,但是由于有些情况比较复杂,顶部的值变化情况较多。但是最小的值始终处于增大状态,所以我们定义(f_{x})表示最小值(x ightarrow k)就行了。
    然后就有(displaystyle f_x=1+frac{1}{2}f_x+frac{1}{2}f_{x+1}),由于(f_k=0),易知(f_x=2cdot(k-x))
    上述情况是在(x<kleq yleq z)的情况下,(xleq y<kleq z)的情况不好处理。这时我们其实可以直接枚举所有可能情况就行,比如我枚举(i)次使得(x)先到达(k)或者让(y)先到达,那么问题就转化为了第一种情况。由于我是枚举所有可能情况,所以概率也很好算。
    细节见代码:

    Code
    // Author : heyuhhh
    // Created Time : 2020/08/07 20:33:27
    #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 = 1e6 + 5, MOD = 998244353;
    
    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) {
        if (n < m) return 0;
        return 1ll * fac[n] * inv[m] % MOD * inv[n - m] % MOD;
    }
    void run() {
        vector<int> a(3);
        int sum = 0;
        int k;
        for (int i = 0; i < 3; i++) {
            cin >> a[i];
            sum += a[i];
        }
        if (sum % 3 != 0) {
            cout << -1 << '
    ';
            return;
        }
        
        sort(all(a));
        k = sum / 3;
        if (a[1] >= k) {
            cout << 2 * (k - a[0]) << '
    ';
            return;
        }
        int d1 = k - a[0], d2 = k - a[1];
        int ans = 0;
        int inv2 = qpow(2, MOD - 2);
        int tt = inv2;
        for (int i = 1, res; i < d1 + d2; i++) {
            res = 1ll * tt * C(i - 1, d1 - 1) % MOD * (i + 2 * (d1 + d2 - i)) % MOD;
            ans = (ans + res) % MOD;
            res = 1ll * tt * C(i - 1, d2 - 1) % MOD * (i + 2 * (d1 + d2 - i)) % MOD;
            ans = (ans + res) % MOD;
            tt = 1ll * tt * inv2 % MOD;
        }
        cout << ans << '
    ';
    }
    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);
        init();
        int T; cin >> T; while(T--)
        run();
        return 0;
    }
    

    E. Fragrant numbers

    猜测仅有前面几个数即可组合出所有的情况,并且最后的数的集合不大,所以直接暴力区间dp转移,打个表然后交就行。

    F. A Very Easy Graph Problem

    最后答案是一颗生成树,那么就将问题转化为了在生成树上统计黑白点对的问题。

    Code
    // Author : heyuhhh
    // Created Time : 2020/08/06 12:24:17
    #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, MOD = 1e9 + 7;
    
    int pow2[N];
    
    void init() {
        pow2[0] = 1;
        for (int i = 1; i < N; i++) {
            pow2[i] = 1ll * pow2[i - 1] * 2 % MOD;
        }
    }
    
    int n, m;
    vector<pii> G[N];
    int f[N];
    int find(int x) {
        return f[x] == x ? f[x] : f[x] = find(f[x]);
    }
    
    bool merge(int x, int y) {
        int fx = find(x), fy = find(y);
        if (fx != fy) {
            f[fx] = fy;
            return true;
        }
        return false;
    }
    
    void add(int& x, int y) {
        x += y;
        if (x >= MOD) x -= MOD;
    }
    
    void del(int& x, int y) {
        x -= y;
        if (x < 0) x += MOD;
    }
    
    int sz[N];
    int a[N], tot;
    int sum;
    
    void dfs(int u, int fa) {
        sz[u] = 1 - a[u];
        for (auto it : G[u]) {
            int v = it.fi, w = it.se;
            if (v != fa) {
                dfs(v, u);
                add(sum, 1ll * w * sz[v] % MOD);
                sz[u] += sz[v];
            }
        }
    }
    
    int ans;
    
    void dfs2(int u, int fa) {
        if (a[u] == 1) {
            add(ans, sum);
        }
        for (auto it : G[u]) {
            int v = it.fi, w = it.se;
            if (v != fa) {
                add(sum, 1ll * w * (tot - sz[v]) % MOD);
                del(sum, 1ll * w * sz[v] % MOD);
    
                dfs2(v, u);
    
                del(sum, 1ll * w * (tot - sz[v]) % MOD);
                add(sum, 1ll * w * sz[v] % MOD);
            }
        }
    }
    
    void run() {
        cin >> n >> m;
        for (int i = 1; i <= n; i++) {
            f[i] = i;
            G[i].clear();
        }
        ans = sum = tot = 0;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            tot += 1 - a[i];
        }
        for (int i = 1; i <= m; i++) {
            int u, v;
            cin >> u >> v;
            if (merge(u, v)) {
                G[u].push_back(MP(v, pow2[i]));
                G[v].push_back(MP(u, pow2[i]));
            }
        }
        dfs(1, 0);
        dfs2(1, 0);
        cout << ans << '
    ';
    }
    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);
        init();
        int T; cin >> T; while(T--)
        run();
        return 0;
    }
    

    G. A Very Easy Math Problem

    题意:
    给定(n,x,k),求:

    [sum_{a_1=1}^nsum_{a_2=1}^ncdotssum_{a_x=1}^n(prod_{j=1}^xa_j^k)f(gcd(...))gcd(...) ]

    其中(f(t))表示是否存在一个大于(1)的数(p),使得(p^2|t)(gcd)我就直接简写了。

    思路:
    这种题就是推推推,都是套路。

    [egin{aligned} &sum_{a_1=1}^nsum_{a_2=1}^ncdotssum_{a_x=1}^n(prod_{j=1}^xa_j^k)f(gcd(...))gcd(...)\ =&sum_{d=1}^nsum_{a_1=1}^nsum_{a_2=1}^ncdotssum_{a_x=1}^n(prod_{j=1}^xa_j^k)f(d)d[gcd(...)==d]\ =&sum_{d=1}^nd^{kx}sum_{a_1=1}^{n/d}sum_{a_2=1}^{n/d}cdotssum_{a_x=1}^{n/d}(prod_{j=1}^xa_j^k)f(d)d[gcd(...)==1]\ =&sum_{d=1}^nd^{kx}sum_{a_1=1}^{n/d}sum_{a_2=1}^{n/d}cdotssum_{a_x=1}^{n/d}(prod_{j=1}^xa_j^k)f(d)dsum_{t|...}mu(t)\ =&sum_{t=1}^nmu(t)t^{kx}sum_{d=1}^nd^{kx}sum_{a_1=1}^{n/td}sum_{a_2=1}^{n/td}cdotssum_{a_x=1}^{n/td}(prod_{j=1}^xa_j^k)f(d)d\ =&sum_{T=1}^nT^{kx}sum_{t|T}mu(t)F(T/t)frac{T}{t}S(n/T)\ =&sum_{T=1}^nT^{kx}S(n/T)sum_{t|T}mu(t)F(T/t)frac{T}{t} end{aligned} ]

    然后后面一部分通过类似于调和级数的东西预处理出来,(S)也可以直接预处理。
    那么数论分块一下就没了。

    Code
    // Author : heyuhhh
    // Created Time : 2020/08/06 14:43:02
    #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 = 2e5 + 5, MOD = 1e9 + 7;
    
    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 mu[N], p[N], phi[N];
    bool chk[N];
    void init() {
        mu[1] = phi[1] = 1;
        int cnt = 0, k = N - 1;
        for(int i = 2; i <= k; i++) {
            if(!chk[i]) p[++cnt] = i, mu[i] = -1, phi[i] = i - 1;
            for(int j = 1; j <= cnt && i * p[j] <= k; j++) {
                chk[i * p[j]] = 1;
                if(i % p[j] == 0) {mu[i * p[j]] = 0; phi[i * p[j]] = phi[i] * p[j]; break;}
                mu[i * p[j]] = -mu[i]; phi[i * p[j]] = phi[i] * (p[j] - 1);
            }
        }
    }
    
    bool f[N];
    int F[N], G[N];
    int S[N], val[N];
    
    void run() {
        int T, k, x;
        cin >> T >> k >> x;
        for (int i = 1; i < N; i++) {
            f[i] = 1;
        }
        for (int t = 2; 1ll * t * t < N; t++) {
            for (int i = 1; 1ll * t * t * i < N; i++) {
                f[i * t * t] = 0;
            }
        }
        for (int i = 1; i < N; i++) {
            for (int j = i; j < N; j += i) {
                F[j] = (F[j] + 1ll * mu[i] * f[j / i] % MOD * (j / i) % MOD + MOD) % MOD;
            }
        }
        for (int i = 1; i < N; i++) {
            F[i] = 1ll * F[i] * qpow(i, 1ll * k * x) % MOD;
            G[i] = (G[i - 1] + F[i]) % MOD;
        }
        for (int i = 1; i < N; i++) {
            val[i] = (val[i - 1] + qpow(i, k)) % MOD;
            S[i] = qpow(val[i], x);
        }
        while (T--) {
            int n;
            cin >> n;
            int ans = 0;
            for (int l = 1, r; l <= n; l = r + 1) {
                r = n / (n / l);
                ans = (ans + 1ll * S[n / l] * (G[r] - G[l - 1] + MOD) % MOD) % MOD;
            }
            cout << ans << '
    ';
        }
    }
    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);
        init();
        run();
        return 0;
    }
    

    H. Yukikaze and Smooth numbers

    题意:
    定义一个(k-number)为所有的质因子大小都不超过(k)的数。
    现在问(1)~(n)中有多少个(k-number)

    思路:
    趣题,考察对min25筛的理解。
    考虑通过min25筛直接筛出符合条件的数的个数。
    仔细思考min25筛的第二部分,我们是分两种情况统计,质数和合数,质数直接利用预处理得到的信息计算,合数则是枚举递归下去(完全积性)。合数部分是不变的,一般只需要修改质数部分,这里我们直接把大于k的去掉就行了。
    因为要统计前缀质数个数,所以第一部分就预处理质数个数和就行。
    细节见代码:

    Code
    // Author : heyuhhh
    // Created Time : 2020/08/07 16:50:26
    #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 << "]
    ";}
    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;
    
    ll n, k;
    ll sum1[N], sum2[N], prime[N];
    ll w[N], ind1[N], ind2[N];
    ll g1[N], g2[N];
    bool chk[N];
    int tot, cnt;
    void pre(int n) { //  sqrt
        chk[1] = 1;
        for(int i = 1; i <= n; i++) {
            if(!chk[i]) {
                prime[++tot] = i;
                sum1[tot] = sum1[tot - 1] + 1;
            }
            for(int j = 1; j <= tot && prime[j] * i <= n; j++) {
                chk[i * prime[j]] = 1;
                if(i % prime[j] == 0) break;
            }
        }
    }
    
    int f(int x, int y) {
        return x <= k;
    }
    
    void calc_g(ll n) {
        int z = sqrt(n + 0.5);
        for(ll i = 1, j; i <= n; i = j + 1) {
            j = n / (n / i);
            w[++cnt] = n / i;
            
            g1[cnt] = w[cnt] - 1;
    
            if(n / i <= z) ind1[n / i] = cnt;
            else ind2[n / (n / i)] = cnt;
        }
        for(int i = 1; i <= tot; i++) {
            for(int j = 1; j <= cnt && prime[i] * prime[i] <= w[j]; j++) {
                ll tmp = w[j] / prime[i], k;
                if(tmp <= z) k = ind1[tmp]; else k = ind2[n / tmp];
                g1[j] -= (g1[k] - sum1[i - 1]); 
            }
        }
    }
    
    ll num;
    
    ll S(ll x, int y) { // 2~x >= P_y
        if(x <= 1 || prime[y] > x) return 0;
        int z = sqrt(n + 0.5);
        ll ans;
        if (k <= x) {
            ans = num - sum1[y - 1];
        } else {
            ll t = (x <= z ? ind1[x] : ind2[n / x]);
            ans = g1[t] - sum1[y - 1];
        }        
        ans = max(ans, 0ll);
        ll tmp = ans;
        for(int i = y; i <= tot && prime[i] * prime[i] <= x ; i++) {
            ll pe = prime[i];
            for(int e = 1; pe * prime[i] <= x; ++e, pe = pe * prime[i]) {
                ans += f(prime[i], e) * S(x / pe, i + 1) + f(prime[i], e + 1);
            }
        }
        return ans;
    } 
    
    void run() {
        tot = cnt = 0;
        memset(chk, 0, sizeof(chk));
        cin >> n >> k;
        int z = sqrt(k + 0.5);
        pre(z);
        calc_g(k);
        num = g1[ind2[1]];
    
        tot = cnt = 0;
        memset(chk, 0, sizeof(chk));
        z = sqrt(n + 0.5);
        pre(z);
        calc_g(n);
        cout << S(n, 1) + 1 << '
    ';
    }
    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;
    }
    

    I. Divisibility

    签到。

    J. Expectation

    根据期望的线性性质,可以直接对二进制拆位然后求每一位对答案的贡献。
    那么对于每一位找出所有这一位为(1)的边然后跑个生成树计数即是有贡献的所有情况,总情况也是对于所有边跑个生成树计数。
    那么概率易求,乘以权值就是当前这一位对答案的贡献。
    注意邻接矩阵是广义的邻接矩阵。

    Code
    // Author : heyuhhh
    // Created Time : 2020/08/06 13:38: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;
    //head
    const int N = 100 + 5, M = 10000 + 5, MOD = 998244353;
    
    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 n, m;
    
    ll b[N][N];
    int g[N][N];
    ll Det(int n){
        int i,j,k;
        ll ret = 1;
        for(i=2;i<=n;i++){
            for(j = i+1;j <= n;j++){
                while(b[j][i]){
                    ll tmp=b[i][i]/b[j][i];//不存在除不尽的情况
                    for(k = i;k <= n;k++){
                        b[i][k] = (b[i][k] - tmp*b[j][k])%MOD;
                        if(b[i][k]<0) b[i][k]+=MOD;
                    }
                    swap(b[i],b[j]);
                    ret = -ret;
                }
            }
            if(!b[i][i]) return -1;
            ret = ret * b[i][i]%MOD;
        }
        if(ret < 0) ret += MOD;
        return ret;
    }
    
    int f[N];
    int find(int x) {
        return f[x] == x ? f[x] : f[x] = find(f[x]);
    }
    
    bool merge(int x, int y) {
        int fx = find(x), fy = find(y);
        if (fx != fy) {
            f[fx] = fy;
            return true;
        }
        return false;
    }
    
    void run() {
        cin >> n >> m;
        vector<pair<pii, int>> edges;
        memset(b, 0, sizeof(b));
        memset(g, 0, sizeof(g));
        for (int i = 1; i <= m; i++) {
            int u, v, w;
            cin >> u >> v >> w;
            edges.push_back(MP(MP(u, v), w));
            ++g[u][v], ++g[v][u];
            ++b[u][u], ++b[v][v];
            --b[u][v], --b[v][u];
        }
        int ans = 0;
        ll tot = Det(n);
        int fm = qpow(tot, MOD - 2);
        for (int k = 30; k >= 0; k--) {
            memset(b, 0, sizeof(b));
            memset(g, 0, sizeof(g));
            for (int i = 1; i <= n; i++) {
                f[i] = i;
            }
            int cnt = 0;
            for (auto& it : edges) {
                int u = it.fi.fi, v = it.fi.se, w = it.se;
                if (w >> k & 1) {
                    ++g[u][v], ++g[v][u];
                    ++b[u][u], ++b[v][v];
                    --b[u][v], --b[v][u];
                    if (merge(u, v)) ++cnt;
                }
            }
            if (cnt < n - 1) continue;
            ll q = Det(n);
            if (q == -1) continue;
            int res = 1ll * (1 << k) * q % MOD * fm % MOD;
            ans = (ans + res) % MOD;
        }
        cout << ans << '
    ';
    }
    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;
    }
    
  • 相关阅读:
    du
    date
    echo
    redis的多实例
    redis相关配置
    mariadb的安装与主从复制
    11.Flask-钩子函数
    Python之Linux下的virtualenv&&virtualenvwrapper
    Linux的防火墙概念
    安装ipython解释器
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/13463514.html
Copyright © 2020-2023  润新知