zoukankan      html  css  js  c++  java
  • 2019 Multi-University Training Contest 3

    2019 Multi-University Training Contest 3

    题目链接

    Blow up the city

    首先考虑建立一个虚根,与所有反图中入度为(0)的点连边形成一颗树,然后考虑建出其支配树。对于(DAG)来说比较简单,反图中按着拓扑序来搞,这样就可以保证处理一个点时,其父亲们都在支配树中了,一个点在支配树中的父亲就是所有能到达它的点在支配树中的(lca)
    支配树有一个性质就是,一个结点到其根这条链上所有的点都支配它,那么根据这个性质处理出深度,利用树上倍增求(lca),可以做到单词询问(O(logn))的复杂度。
    代码如下:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 5, M = 2e5 + 5;
    int T;
    vector <int> G[N], rG[N];
    int n, m, tot;
    int d[N], f[N][20], a[N], dep[N];
    int lca(int x, int y) {
        if(dep[y] > dep[x]) swap(x, y);
        for(int i = 19; i >= 0; i--) {
            if(dep[f[x][i - 1]] >= dep[y]) x = f[x][i - 1];
        }
        if(x == y) return x;
        for(int i = 19; i >= 0; i--) {
            if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
        }
        return f[x][0];
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> T;
        while(T--) {
            cin >> n >> m;
            for(int i = 1; i <= n + 1; i++)
                G[i].clear(), rG[i].clear(), d[i] = 0;
            for(int i = 1; i <= m; i++) {
                int u, v; cin >> u >> v;
                rG[v].push_back(u);
                G[u].push_back(v);
                d[u]++;
            }
            queue <int> q;
            for(int i = 1; i <= n; i++) {
                if(!d[i]) {
                    q.push(i);
                    G[i].push_back(n + 1);
                }
            }
            tot = 0; dep[n + 1] = 0;
            while(!q.empty()) {
                int u = q.front(); q.pop();
                a[++tot] = u;
                for(auto v : rG[u]) {
                    if(--d[v] == 0) q.push(v);
                }
            }
            for(int i = 1; i <= tot; i++) {
                int x = a[i], p = -1;
                for(auto y : G[x]) {
                    if(p == -1) p = y;
                    else p = lca(p, y);
                }
                f[x][0] = p; dep[x] = dep[p] + 1;
                for(int j = 1; j < 20; j++) f[x][j] = f[f[x][j - 1]][j - 1] ;
            }
            int Q; cin >> Q;
            while(Q--) {
                int x, y; cin >> x >> y;
                int ans = dep[x] + dep[y] - dep[lca(x, y)];
                cout << ans << '
    ';
            }
        }
        return 0;
    }
    

    Distribution of books

    如果(a_i)只能为正的话就是一个很简单的二分答案了,但这里能够为负数,也就是说之前贪心地来选取是不行的。所以我们就(dp)一下处理出前(i)个数能分配的最大组数就行了,转移的时候用线段树优化一下。

    Code
    #include <bits/stdc++.h>
    #define INF 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    const int N = 4e5 + 5;
    ll b[N], a[N];
    int T, D, n, k;
    int get(ll x) {
        return lower_bound(b + 1, b + D + 1, x) - b;
    }
    int mx[N << 2];
    void push_up(int o) {
        mx[o] = max(mx[o << 1], mx[o << 1|1]);
    }
    void build(int o, int l, int r) {
        mx[o] = -INF;
        if(l == r) return;
        int mid = (l + r) >> 1;
        build(o << 1, l, mid); build(o << 1|1, mid + 1, r);
    }
    void update(int o, int l, int r, int pos, int v) {
        if(l == r && l == pos) {
            mx[o] = max(mx[o], v);
            return ;
        }
        int mid = (l + r) >> 1;
        if(pos <= mid) update(o << 1, l, mid, pos, v);
        else update(o << 1|1, mid + 1, r, pos, v);
        push_up(o);
    }
    int query(int o, int l, int r, int v) {
        if(l >= v) return mx[o];
        int mid = (l + r) >> 1;
        int ans = query(o << 1|1, mid + 1, r, v);
        if(v <= mid) ans = max(ans, query(o << 1, l, mid, v));
        return ans;
    }
    bool check(ll x) {
        D = 0;
        for(int i = 1; i <= n; i++) {
            b[++D] = a[i];
            b[++D] = a[i] - x;
        }
        b[++D] = 0;
        sort(b + 1, b + D + 1);
        D = unique(b + 1, b + D + 1) - b - 1;
        build(1, 1, D);
        update(1, 1, D, get(0), 0);
        for(int i = 1; i <= n; i++) {
            int f = query(1, 1, D, get(a[i] - x)) + 1;
            if(f >= k) return 1;
            update(1, 1, D, get(a[i]), f);
        }
        return 0;
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> T;
        while(T--) {
            cin >> n >> k;
            for(int i = 1; i <= n; i++) cin >> a[i], a[i] += a[i - 1];
            ll l = -2e14, r = 1e9 + 1, mid;
            while(l < r) {
                mid = (l + r) >> 1;
                if(check(mid)) r = mid;
                else l = mid + 1;
            }
            cout << l << '
    ';
        }
        return 0;
    }
    

    Fansblog

    考虑威尔逊定理:对于一个质数(p),有((p-1)!=p-1() mod (p)),并且还需要知道素数的间隔一般来讲是不超过(264)的(可能记忆有点错误)。所以就暴力找上一个素数,然后暴力求逆元来搞就行了。
    代码如下:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef __int128 ll;
    ll p;
    int T;
    struct Istream {
        template <class T>
        Istream &operator >>(T &x) {
            static char ch;static bool neg;
            for(ch=neg=0;ch<'0' || '9'<ch;neg|=ch=='-',ch=getchar());
            for(x=0;'0'<=ch && ch<='9';(x*=10)+=ch-'0',ch=getchar());
            x=neg?-x:x;
            return *this;
        }
    }fin;
    
    struct Ostream {
        template <class T>
        Ostream &operator <<(T x) {
            x<0 && (putchar('-'),x=-x);
            static char stack[233];static int top;
            for(top=0;x;stack[++top]=x%10+'0',x/=10);
            for(top==0 && (stack[top=1]='0');top;putchar(stack[top--]));
            return *this;
        }
    
        Ostream &operator <<(char ch) {
            putchar(ch);
            return *this;
        }
    }fout;
    
    bool Is_prime(long long x) {
        bool ok = true;
        for(int i = 2; (long long)i * i <= x; i++) {
            if(x % i == 0) {
                ok = false;
                break ;
            }
        }
        return ok;
    }
    ll qp(ll a, long long b) {
        ll ans = 1;
        while(b) {
            if(b & 1) ans = ans * a % p;
            a = a * a % p;
            b >>= 1;
        }
        return ans;
    }
    int main() {
        cin >> T;
        while(T--) {
            fin >> p;
            ll ans = p - 1;
            long long q;
            for(long long i = p - 1;;i--) {
                if(Is_prime(i)) {
                    q = i; break;
                }
            }
            for(long long i = q + 1; i <= p - 1; i++) {
                ans = ans % p * qp(i, p - 2) % p;
            }
            fout << ans << '
    ';
        }
        return 0;
    }
    

    Find the answer

    队友写的伸展树。。实际上不用这么麻烦,考虑将问题转换一下:保留最小的几个数使得满足(sum_{j=1}^{i-1}W_jleq m-W_i)。因为这是前缀,所以直接用线段树搞一搞即可。
    还是给队友写的代码吧:

    Code
    #include<bits/stdc++.h>
    typedef long long ll;
    const int MAXN = 2e5 + 5, MAXM = 2e5 + 5, INF = 0x3f3f3f3f, MOD = 998244353;
    const ll INFL = 0x3f3f3f3f3f3f3f3f;
    using namespace std;
    const int oo = (1e9) - (1e6);
    #define lson o<<1,l,m
    #define rson o<<1|1,m+1,r
    #define mid l + ((r-l)>>1)
    #define pb push_back
    #define RR register
    #define random(a,b) ((a)+rand()%((b)-(a)+1))
    #define all(v) (v.begin(),v.end())
    #define lc(x) c[x][0]
    #define rc(x) c[x][1]
    typedef long double db;
    typedef unsigned int uint;
    int t, n, m, a;
    namespace Rin {
        int c[MAXN][2], fa[MAXN], siz[MAXN], cnt[MAXN], rt, tot = 0;
        ll v[MAXN], sum[MAXN];
        void init() {
            rt = tot = 0;
            memset(siz, 0, sizeof(siz));
            memset(cnt, 0, sizeof(cnt));
            memset(v, 0, sizeof(v));
            memset(sum, 0, sizeof(sum));
            memset(c, 0, sizeof(c));
            memset(fa, 0, sizeof(c));
        }
        void pushUp(int x) {
            siz[x] = siz[lc(x)] + siz[rc(x)] + cnt[x];
            sum[x] = sum[lc(x)] + sum[rc(x)] + v[x] * cnt[x];
        }
        inline void connect(int x, int f, int k) {
            c[f][k] = x;
            fa[x] = f;
        }
        void rotate(int x) {
            int y = fa[x], z = fa[y], k = x == c[y][1];
            connect(c[x][k ^ 1], y, k);
            connect(y, x, k ^ 1);
            connect(x, z, y == c[z][1]);
            pushUp(y); pushUp(x);
        }
    
        void splay(int x, int t) {
            while (fa[x] != t) {
                int y = fa[x], z = fa[y];
                if (z != t)(x == c[y][0]) ^ (y == c[z][0]) ? rotate(x) : rotate(y);
                rotate(x);
            }
            if (t == 0)rt = x;
        }
        void insert(int x) {
            int u = rt, f = 0;
            while (u) {
                f = u; siz[u]++; sum[u] += x;
                u = c[u][x > v[u]];
            }
            if (!u) {
                u = ++tot;
                siz[u] = cnt[u] = 1;
                v[u] = sum[u] = x;
                fa[u] = f;
                if (f)c[f][x > v[f]] = u;
            }
            splay(u, 0);
        }
        int find(int x, ll k) {
            k += INF;
            int ans = 0;
            while (1) {
                if (sum[c[x][1]] >= k)x = c[x][1];
                else if (sum[c[x][1]] + v[x] * cnt[x] < k)k -= sum[c[x][1]] + v[x] * cnt[x], ans += siz[c[x][1]] + cnt[x], x = c[x][0];
                else return ans += (k - sum[c[x][1]] + v[x] - 1) / v[x] + siz[c[x][1]];
            }
        }
    }
    
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> t;
        while (t--) {
            cin >> n >> m;
            Rin::init();
            Rin::insert(0);
            Rin::insert(INF);
            ll sum = 0;
            for (int i = 1; i <= n; i++) {
                cin >> a;
                sum += a;
                if (sum <= m) {
                    cout << "0" << " ";
                    Rin::insert(a);
                    continue;
                }
                cout << Rin::find(Rin::rt, sum - m) - 1 << " ";
                Rin::insert(a);
            }
            cout << '
    ';
        }
        return 0;
    }
    

    Game

    题目问的是先手必赢的情况,我们知道先手必输的情况就是区间中所有数的异或和为0,由于这个比较好求,所以可以将问题稍微转换一下,即求区间中异或和为0的个数。
    因为多个询问并且不强制在线,并且一个数的贡献比较好算,那就考虑莫队了。2操作也只会影响一个位置的前缀异或和,也算是比较好写的。
    注意一下区间变成了([l-1,r]),结合一下前缀异或和理解一下就行了。
    代码如下:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 5;
    int a[N], sum[N], upd[N];
    ll cnt[(1 << 21) + 5];
    int n, m, block;
    int l, r, t;
    ll ans;
    struct query{
        int id, l, r, k;
        ll ans;
    }q[N];
    int blo(int x) {
        return (x - 1) / block;
    }
    bool cmp(query A, query B) {
        if(blo(A.l) == blo(B.l) && blo(A.r) == blo(B.r)) return blo(A.k) < blo(B.k);
        if(blo(A.l) == blo(B.l)) return blo(A.r) < blo(B.r);
        return blo(A.l) < blo(B.l);
    }
    void add(int p, int v) {
        if(v == 1) ans += cnt[sum[p]]++;
        else ans -= --cnt[sum[p]];
    }
    void Update(int pos) {
        if(l <= pos && pos <= r) add(pos, -1);
        sum[pos] ^= a[pos];
        swap(a[pos], a[pos + 1]);
        sum[pos] ^= a[pos];
        if(l <= pos && pos <= r) add(pos, 1);
    }
    
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        while(cin >> n >> m) {
            memset(cnt, 0, sizeof(cnt));
            block = (int)pow(n, 0.66666);
            for(int i = 1; i <= n; i++) cin >> a[i], sum[i] = sum[i - 1] ^ a[i];
            int tot = 0, num = 0;
            for(int i = 1; i <= m; i++) {
                int op; cin >> op;
                if(op == 1) {
                    int l, r; cin >> l >> r; ;
                    q[++tot].id = tot;
                    q[tot].l = l - 1; q[tot].r = r;
                    q[tot].k = num;
                } else {
                    int p; cin >> p;
                    upd[++num] = p;
                }
            }
            sort(q + 1, q + tot + 1, cmp);
            l = t = 0; r = -1; ans = 0;
            for(int i = 1; i <= tot; i++) {
                
                for(; r < q[i].r; r++) add(r + 1, 1);
                for(; r > q[i].r; r--) add(r, -1);
                for(; l < q[i].l; l++) add(l, -1);
                for(; l > q[i].l; l--) add(l - 1, 1);
    for(; t < q[i].k; t++) Update(upd[t + 1]);
                for(; t > q[i].k; t--) Update(upd[t]);
                q[q[i].id].ans = 1ll * (r - l + 1) * (r - l) / 2 - ans;
            }
            for(int i = 1; i <= tot; i++) cout << q[i].ans << '
    ';
        }
        return 0;
    }
    

    K Subsequence

    网络流的一个经典应用。

    Code
    #include <bits/stdc++.h>
    #define INF 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    const int N = 2015;
    struct edge {
        int to, capacity, cost, rev;
        edge() {}
        edge(int to, int _capacity, int _cost, int _rev) :to(to), capacity(_capacity), cost(_cost), rev(_rev) {}
    };
    struct Min_Cost_Max_Flow {
        int V, H[N << 1], dis[N << 1], PreV[N << 1], PreE[N << 1];
        vector<edge> G[N << 1];
        void Init(int n) {
            V = n;
            for (int i = 0; i <= V; ++i)G[i].clear();
        }
        void Add_Edge(int from, int to, int cap, int cost) {
            G[from].push_back(edge(to, cap, cost, G[to].size()));
            G[to].push_back(edge(from, 0, -cost, G[from].size() - 1));
        }
    //flow是自己传进去的变量,就是最后的最大流,返回的是最小费用,f=INF
        int Min_cost_max_flow(int s, int t, int f, int& flow) {
            int res = 0; fill(H, H + 1 + V, 0);
            while (f) {
                priority_queue <pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>> > q;
                fill(dis, dis + 1 + V, INF);
                dis[s] = 0; q.push(pair<int, int>(0, s));
                while (!q.empty()) {
                    pair<int, int> now = q.top(); q.pop();
                    int v = now.second;
                    if (dis[v] < now.first)continue;
                    for (int i = 0; i < G[v].size(); ++i) {
                        edge& e = G[v][i];
                        if (e.capacity > 0 && dis[e.to] > dis[v] + e.cost + H[v] - H[e.to]) {
                            dis[e.to] = dis[v] + e.cost + H[v] - H[e.to];
                            PreV[e.to] = v;
                            PreE[e.to] = i;
                            q.push(pair<int, int>(dis[e.to], e.to));
                        }
                    }
                }
                if (dis[t] == INF)break;
                for (int i = 0; i <= V; ++i)H[i] += dis[i];
                int d = f;
                for (int v = t; v != s; v = PreV[v])d = min(d, G[PreV[v]][PreE[v]].capacity);
                f -= d; flow += d; res += d*H[t];
                for (int v = t; v != s; v = PreV[v]) {
                    edge& e = G[PreV[v]][PreE[v]];
                    e.capacity -= d;
                    G[v][e.rev].capacity += d;
                }
            }
            return res;
        }
        int Max_cost_max_flow(int s, int t, int f, int& flow) {
            int res = 0;
            fill(H, H + 1 + V, 0);
            while (f) {
                priority_queue <pair<int, int>> q;
                fill(dis, dis + 1 + V, -INF);
                dis[s] = 0;
                q.push(pair<int, int>(0, s));
                while (!q.empty()) {
                    pair<int, int> now = q.top(); q.pop();
                    int v = now.second;
                    if (dis[v] > now.first)continue;
                    for (int i = 0; i < G[v].size(); ++i) {
                        edge& e = G[v][i];
                        if (e.capacity > 0 && dis[e.to] < dis[v] + e.cost + H[v] - H[e.to]) {
                            dis[e.to] = dis[v] + e.cost + H[v] - H[e.to];
                            PreV[e.to] = v;
                            PreE[e.to] = i;
                            q.push(pair<int, int>(dis[e.to], e.to));
                        }
                    }
                }
                if (dis[t] == -INF)break;
                for (int i = 0; i <= V; ++i)H[i] += dis[i];
                int d = f;
                for (int v = t; v != s; v = PreV[v])d = min(d, G[PreV[v]][PreE[v]].capacity);
                f -= d; flow += d;
                res += d*H[t];
                for (int v = t; v != s; v = PreV[v]) {
                    edge& e = G[PreV[v]][PreE[v]];
                    e.capacity -= d;
                    G[v][e.rev].capacity += d;
                }
            }
            return res;
        }
    }sol;
    int T, n, k;
    int a[N];
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> T;
        while(T--) {
            cin >> n >> k;
            for(int i = 1; i <= n; i++) cin >> a[i];
            int s1 = 0, s2 = 2 * n + 1, t = 2 * n + 2;
            sol.Init(t);
            sol.Add_Edge(s1, s2, k, 0);
            for(int i = 1; i <= n; i++) {
                sol.Add_Edge(i, i + n, 1, -a[i]);
            }
            for(int i = 1; i <= n; i++) {
                for(int j = i + 1; j <= n; j++) {
                    if(a[j] >= a[i]) sol.Add_Edge(i + n, j, 1, 0);
                }
            }
            for(int i = 1; i <= n; i++) {
                sol.Add_Edge(s2, i, INF, 0);
                sol.Add_Edge(i + n, t, INF, 0);
            }
            int flow = 0;
            cout << -sol.Min_cost_max_flow(s1, t, INF, flow) << '
    ';
        }
        return 0;
    }
    

    Squrirrel

    考虑不删边的情况,那么我们就需要通过两次dfs来进行dp,并且要维护子树内最远距离和次远距离以及从父亲那边的最远距离。
    因为在这种题目中,维护最大距离时,肯定是优先考虑最大的那几个的。
    删边的话就再加一维dp状态就好了。并且还要维护第三大的距离。
    代码里面的(g)数组就表示在子树中删边的最大、次大距离,(f)数组是不删边的情况,(h)数组就是从父亲那边转移过来时的最大距离,(0/1)分别代表不删边和删边。
    详见代码:

    Code
    #include <bits/stdc++.h>
    #define mp make_pair
    #define fi first
    #define se second
    #define INF 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    const int N = 2e5 + 5;
    int T, n;
    struct Edge{
        int v, w, next;
    }e[N << 1];
    int head[N], tot;
    void adde(int u, int v, int w) {
        e[tot].v = v; e[tot].w = w; e[tot].next = head[u]; head[u] = tot++;
    }
    pii f[N][3];
    int g[N][2], h[N][2];
    int d[N];
    pii res;
    void init() {
        for(int i = 1; i <= n; i++) {
            for(int j = 0; j < 2; j++) {
                f[i][j] = mp(0, 0);
                g[i][j] = g[i][j] = 0;
            }
            f[i][3] = mp(0, 0);
        }
        res = mp(INF, INF);
    }
    void dfs(int u, int fa) {
        for(int i = head[u]; i != -1; i = e[i].next) {
            int v = e[i].v, w = e[i].w;
            if(v == fa) continue;
            d[v] = w;
            dfs(v, u);
            if(f[v][0].fi + w > f[u][0].fi) {
                f[u][2] = f[u][1];
                f[u][1] = f[u][0];
                f[u][0].fi = f[v][0].fi + w;
                f[u][0].se = v;
            } else if(f[v][0].fi + w > f[u][1].fi) {
                f[u][2] = f[u][1];
                f[u][1].fi = f[v][0].fi + w;
                f[u][1].se = v;
            } else if(f[v][0].fi + w > f[u][2].fi) {
                f[u][2].fi = f[v][0].fi + w;
                f[u][2].se = v;
            }
        }
        int v0 = f[u][0].se, v1 = f[u][1].se;
        g[u][0] = min(f[v0][0].fi, max(f[v0][1].fi, g[v0][0]) + d[v0]);
        g[u][1] = min(f[v1][0].fi, max(f[v1][1].fi, g[v1][0]) + d[v1]);
    }
    void dfs2(int u, int fa) {
        pii tmp = mp(min(max(h[u][0], max(g[u][0], f[u][1].fi)), max(h[u][1], f[u][0].fi)), u);
        res = min(res, tmp);
        for(int i = head[u]; i != -1; i = e[i].next) {
            int v = e[i].v;
            if(v == fa) continue ;
            if(v == f[u][0].se) {
                h[v][0] = max(h[u][0], f[u][1].fi) + d[v];
                h[v][1] = max(h[u][1], f[u][1].fi) + d[v];
                h[v][1] = min(h[v][1], max(h[u][0], f[u][1].fi));
                h[v][1] = min(h[v][1], max(max(g[u][1], h[u][0]), f[u][2].fi) + d[v]);
            } else {
                h[v][0] = max(h[u][0], f[u][0].fi) + d[v];
                h[v][1] = max(h[u][1], f[u][0].fi) + d[v];
                if(v == f[u][1].se) h[v][1] = min(h[v][1], max(max(h[u][0], g[u][0]), f[u][2].fi) + d[v]);
                else h[v][1] = min(h[v][1], max(max(h[u][0], g[u][0]), f[u][1].fi) + d[v]);
                h[v][1] = min(h[v][1], max(f[u][0].fi, h[u][0]));
            }
            dfs2(v, u);
        }
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> T;
        while(T--) {
            cin >> n;
            memset(head, -1, sizeof(head)); tot = 0;
            for(int i = 1; i < n; i++) {
                int u, v, w;
                cin >> u >> v >> w;
                adde(u, v, w); adde(v, u, w);
            }
            init();
            dfs(1, -1);
            dfs2(1, -1);
            cout << res.se << ' ' << res.fi << '
    ';
        }
        return 0;
    }
    
  • 相关阅读:
    eclipse下c/cpp " undefined reference to " or "launch failed binary not found"问题
    blockdev 设置文件预读大小
    宝宝语录
    CentOS修改主机名(hostname)
    subprocess报No such file or directory
    用ldap方式访问AD域的的错误解释
    英特尔的VTd技术是什么?
    This virtual machine requires the VMware keyboard support driver which is not installed
    Linux内核的文件预读详细详解
    UNP总结 Chapter 26~29 线程、IP选项、原始套接字、数据链路访问
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/11286533.html
Copyright © 2011-2022 走看看