• 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;
    }
    
  • 相关阅读:
    nor flash之4字节地址模式
    DroidVim:在安卓手机上使用vim
    从linux命令行分享文件:bashupload.com和transfer.sh
    记一个实时Linux的中断线程化问题
    nor flash之写保护开销
    第七届开源操作系统会议(OS2ATC 2019)见闻及资料分享
    nor flash之擦除和写入
    PyCharm 远程调试代码
    【图像分析】形态学
    【强化学习】DQN 算法改进
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/11286533.html
Copyright © 2020-2023  润新知