• [gym] XXII Open Cup, Grand Prix of Daejeon


    A. Points

    题意:

    你要维护两个二维平面上的可重点集U,V,每个点集支持插入,删除,以及查询下面这个式子的操作。

    $$D(U, V) = \min\limits_{\begin{smallmatrix}(u_x, u_y) \in U \\ (v_x, v_y) \in V\end{smallmatrix}}\max(u_x + v_x, u_y + v_y)$$

    题解:

    比较难想到的是拆掉max,我们讨论max取左边的时候是什么条件:

    $$u_x + v_x >= u_y + v_y$$

    我们维护成两个关于u和v自身的量,就是

    $$u_x -u_y>=v_y - v_x$$

    我们把$(u_x - u_y)$作为U点集的点的id,$(v_y-v_x)$作为V点集的点的id。

    我们使用线段树维护答案。

    我们考虑$[l,r]$的答案是什么,我们可以取点的方案是:

    1. 都取左边

    2.都取右边

    3.u取左,v取右

    4.u取右,v取左

    前两种方案是子树答案,我们考虑后两种方案怎么计算答案。我们这样取之后u的id跟v的id的取值不相交,这里就可以用到上面的性质:假如$u_{id} >= v_{id}$,答案就是$u_x + v_x$,否则答案就是$u_y+v_y$。

    所以u取左,v取右的时候,答案一定是$u_x + v_x$,我们只要找出$[l, mid]$中$u_x$的最小值和$[mid + 1, r]$中$v_x$的最小值就是答案。

    情况4与情况3类似,这里不做叙述。

    然后,题目中维护的点集是一个可重集(就算不可重,加入id后也会出现重复)。维护这个重复的线段树不好写,我们可以直接用multiset维护每个id的最小值,更新的时候,我们先找出最小值,然后强制在线段树上更新就行了,线段树就不需要维护集合了。

    这里注意的是multiset的erase方法,如果参数是一个元素的话,他会把所有相同元素删掉!!!WA45就是这个原因。

    所以我们要把S.erase(x)改成S.erase(S.find(x)),这样就只会删一个元素了。

    //
    // Created by onglu on 2022/4/3.
    //
    
    #include <bits/stdc++.h>
    
    #define all(a) a.begin(),a.end()
    #define rall(a) a.rbegin(),a.rend()
    
    #define endl '\n'
    #define lson (rt << 1)
    #define rson (rt << 1 | 1)
    #define Mid ((l + r) / 2)
    //#define int long long
    using namespace std;
    //const int N = 2e6 + 1009;
    const int N = 5e5 + 1009;
    //const int N = 5009;
    //const int N = 309;
    struct node {
        int x, y;
    };
    const int B = 250010;
    int n, m;
    int x[2][N * 4], y[2][N * 4], minn[N * 4];
    void update(int rt) {
        for(int i = 0; i < 2; i++) {
            x[i][rt] = min(x[i][lson], x[i][rson]);
            y[i][rt] = min(y[i][lson], y[i][rson]);
        }
        minn[rt] = min({minn[lson], minn[rson], y[0][lson] + y[1][rson], x[1][lson] + x[0][rson]});
    }
    void build(int l, int r, int rt) {
        if(l == r) {
            for(int i = 0; i < 2; i++) {
                x[i][rt] = 0x3f3f3f3f;
                y[i][rt] = 0x3f3f3f3f;
            }
            minn[rt] = 0x3f3f3f3f;
            return ;
        }
        build(l, Mid, lson); build(Mid + 1, r, rson);
        update(rt);
    }
    void modify(int l, int r, int rt, int f, int a, int b, int pos) {
        if(l == r) {
            x[f][rt] = a;
            y[f][rt] = b;
            minn[rt] = min(y[0][rt] + y[1][rt], x[1][rt] + x[0][rt]);
            return ;
        }
        if(pos <= Mid) modify(l, Mid, lson, f, a, b, pos);
        else modify(Mid + 1, r, rson, f, a, b, pos);
        update(rt);
    }
    multiset<int> Sx[2][N], Sy[2][N];
    void work() {
        cin >> n;
        build(1, N, 1);
        while(n--) {
            int opt, f, xx, yy;
            cin >> opt >> f >> xx >> yy;
            f -= 1;
            int pos = f ? yy - xx : xx - yy;
            pos += B;
            if(opt == 1) {
                Sx[f][pos].insert(xx);
                Sy[f][pos].insert(yy);
                modify(1, N, 1, f, *Sx[f][pos].begin(), *Sy[f][pos].begin(), pos);
            } else {
                Sx[f][pos].erase(Sx[f][pos].find(xx));
                Sy[f][pos].erase(Sy[f][pos].find(yy));
                int a, b;
                a = Sx[f][pos].empty() ? 0x3f3f3f3f : *Sx[f][pos].begin();
                b = Sy[f][pos].empty() ? 0x3f3f3f3f : *Sy[f][pos].begin();
                modify(1, N, 1, f, a, b, pos);
            }
            if(minn[1] > N) {
                cout << -1 << endl;
            } else {
                cout << minn[1] << endl;
            }
        }
    }
    
    signed main() {
    #ifdef LOCAL
        freopen("C:\\Users\\onglu\\CLionProjects\\acm\\data.in", "r", stdin);
        freopen("C:\\Users\\onglu\\CLionProjects\\acm\\data.out", "w", stdout);
    #endif
        ios::sync_with_stdio(false);
        cin.tie(0);
        int Case = 1;
    //    cin >> Case;
        while (Case--) work();
        return 0;
    }
    View Code

    B. Bingo

    题意:

    给定一个n*n的矩形,在里面画k个#,使得没有任意n个#同行或者同列或者在对角线上。

    题解:

    一开始直接占了一个对角线,喜提WA7,原因是偶数的情况,两条对角线没有交点。直接交换一下两个对角线的第一个格子就行了,左下,右上留空,然后中间是从左上到右下留空,其他填满就行,一共能填n*(n-1)格。

    #include <bits/stdc++.h>
    using namespace std;
    int n, k;
    int g[109][109];
    signed main()
    {
        cin >> n >> k;
        if(k == 0) {
            cout << "YES" << endl;
            for(int i = 1; i <= n; i++) {
                for(int j = 1; j <= n; j++) {
                    cout << ".";
                }
                cout << endl;
            }
            return 0;
        }
        if(n == 2) {
            if(k == 1) {
                cout << "YES" << endl;
                cout << "#.\n.." << endl;
            } else {
                cout << "NO" << endl;
            }
            return 0;
        }
        if(k > n * n - n) {
            cout << "NO" << endl;
            return 0;
        }
        cout << "YES" << endl;
        g[1][n] = 1; g[n][1] = 1;
        for(int i = 2; i < n; i++) g[i][i] = 1;
        int cnt = 0;
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= n; j++) {
                if(!g[i][j] && cnt < k) {
                    cout << "#";
                    cnt++;
                } else {
                    cout << ".";
                }
            }
            cout << endl;
        }
        return 0;
    }
    View Code

    C. AND PLUS OR

    题意:

    给定一个$2^n$长度的序列$A$,让你给出一组$i,j$使得$A_i + A_j < A_{i\and j} + A_{i\or j}$.

    题解:

    没做出来,官方题解写的很烂,一大半看不懂啥意思,也不给解释,下面是官方题解的个人理解。

    我们首先对式子进行拆分,把i,j看成集合,按照i,j共有部分,i专有部分,j专有部分拆分成

    $$x = i \cap j, y = i - x, z = j - x$$

    这样之后,原式可以改写成

    $$A(x + y) + A(x + z) < A(x) + A(x + y + z)$$

    $$A(x + y) - A(x) <  + A(x + y + z) - A(x + z)$$

    我们把它看成一个关于集合的函数$f(S) = A(x + y + S) - A(x + S)$,显然原式等价于$f(\emptyset) < f(z)$

    这样变化有什么用呢?

    当x,y固定的时候,假设我们存在一个$z$,满足$f(\emptyset) < f(z)$,那么我们可以尝试在空集中逐位加入z的每一位,观察函数的变化

    也就是我们拆分出$z_j$表示只包含z的前j个1的集合。

    这样有什么用呢,我们发现由于最后满足$f(\emptyset) < f(z)$,在$j = 0 \rightarrow |z|$的过程中,一定存在一个位置$j$,使得$f(z_{j - 1}) < f(z_{j})$。

    如果不满足这个条件的话,原序列显然是小于等于$f(\emptyset)$的。

    这样的话,我们令$x = x + z_j, z' = z_{j + 1 \cdots |z|}$,于是我们得到一个新的解$f'(\emptyset) < f'(z')$。  

    因为无论如何都可以这样变化,那么我们最后一定可以得到$|z| = 1$,也就是说j可以只包含1个不在x中的另外的元素。

    然后我们注意到上述变化,我们只对x和z进行操作,y是没有改变的,并且y跟z是等价的。也就是说我们应用相同的操作在y上,y也可以只包含1个不在x中的元素。

    于是答案就很明确了,枚举x,再枚举两个不包含在x中的元素。

    //
    // Created by onglu on 2022/4/3.
    //
    
    #include <bits/stdc++.h>
    
    #define all(a) a.begin(),a.end()
    #define rall(a) a.rbegin(),a.rend()
    
    #define endl '\n'
    #define lson (rt << 1)
    #define rson (rt << 1 | 1)
    #define Mid ((l + r) / 2)
    //#define int long long
    using namespace std;
    const int N = 2e6 + 1009;
    //const int N = 2e5 + 1009;
    //const int N = 5009;
    //const int N = 309;
    int n, m, a[N];
    
    void work() {
        cin >> n;
        for(int i = 0; i < 1 << n; i++) {
            cin >> a[i];
        }
        for(int x = 0; x < 1 << n; x++) {
            for(int y = 1; y < 1 << n; y <<= 1) if(!(x & y)) {
                for(int z = 1; z < 1 << n; z <<= 1) if(!(x & z) && y != z) {
                    if(a[x + y] + a[x + z] < a[x] + a[x + y + z]) {
                        cout << x + y << " " << x + z << endl;
                        return ;
                    }
                }
            }
        }
        cout << -1 << endl;
        return ;
    }
    
    signed main() {
    #ifdef LOCAL
        freopen("C:\\Users\\onglu\\CLionProjects\\acm\\data.in", "r", stdin);
        freopen("C:\\Users\\onglu\\CLionProjects\\acm\\data.out", "w", stdout);
    #endif
        ios::sync_with_stdio(false);
        cin.tie(0);
        int Case = 1;
    //    cin >> Case;
        while (Case--) work();
        return 0;
    }
    View Code

    E. Yet Another Interval Graph Problem

    题意:

    给定数轴上n条线段,两条线段相交表示他们之间有一条无向边。每个线段有删除的代价,问最小的删边代价使得图中的最大连通块大小不超过k。

    题解:

    一开始想到$f[i][j]$表示前i条边,最后一个连通块为j的最小代价,发现不好转移,原因是没办法知道最后一条边的位置在哪里,也就没办法转移。

    正确做法是:把删边改成取边,这样就可以人工枚举分段点了。

    $f[x]$表示x位置之前的取边的最大答案,那么我们钦定$[x + 1, i]$这一段强制连通,这样我们要使代价最大只需要取最大的k条边就行。向前扫描的时候维护最大k条边,用小根堆维护就行。

    //
    // Created by onglu on 2022/4/3.
    //
    
    #include <bits/stdc++.h>
    
    #define all(a) a.begin(),a.end()
    #define rall(a) a.rbegin(),a.rend()
    
    #define endl '\n'
    #define lson (rt << 1)
    #define rson (rt << 1 | 1)
    #define Mid ((l + r) / 2)
    #define int long long
    using namespace std;
    //const int N = 2e6 + 1009;
    const int N = 2e5 + 1009;
    //const int N = 2509;
    //const int N = 309;
    int n, m;
    int f[N];
    struct node {
        int x, y, w;
    };
    int sum;
    vector<node> v, a[N], tt[N];
    vector<int> t;
    priority_queue<int> q;
    void Insert(int x) {
        if(q.size() < m) {
            sum += x;
            q.push(-x);
        } else if(-q.top() < x) {
            sum -= -q.top();
            q.pop();
            sum += x;
            q.push(-x);
        }
    }
    void work() {
        int ans = 0;
        cin >> n >> m;
        for(int i = 1; i <= n; i++) {
            int a, b, c;
            cin >> a >> b >> c;
            t.push_back(a);
            t.push_back(b);
            v.push_back({a, b, c});
            ans += c;
        }
        std::sort(t.begin(), t.end());
        t.resize(std::unique(t.begin(), t.end()) - t.begin());
        for(int i = 0; i < v.size(); i++) {
            v[i].x = std::lower_bound(t.begin(), t.end(), v[i].x) - t.begin() + 1;
            v[i].y = std::lower_bound(t.begin(), t.end(), v[i].y) - t.begin() + 1;
            a[v[i].y].push_back({v[i].x, v[i].y, v[i].w});
        }
        for(int i = 1; i <= t.size(); i++) {
            sum = 0;
            while(q.size()) q.pop();
            for(int j = 0; j <= i; j++) {
                tt[j].clear();
            }
            for(int j = i; j > 0; j--) {
                for(auto r : a[j]) {
                    tt[r.x].push_back(r);
                }
            }
            for(int j = i; j >= 1; j--) {
                for(auto r : tt[j]) {
                    Insert(r.w);
                }
                f[i] = max(f[i], f[j - 1] + sum);
            }
        }
        cout << ans - f[t.size()] << endl;
    
    }
    
    signed main() {
    #ifdef LOCAL
        freopen("C:\\Users\\onglu\\CLionProjects\\acm\\data.in", "r", stdin);
        freopen("C:\\Users\\onglu\\CLionProjects\\acm\\data.out", "w", stdout);
    #endif
        ios::sync_with_stdio(false);
        cin.tie(0);
        int Case = 1;
    //    cin >> Case;
        while (Case--) work();
        return 0;
    }
    View Code
  • 相关阅读:
    什么是.NET Core以及.NET Core能做什么 菜鸟飞不动
    SQL数据库连接字符串的几种写法整理
    高并发
    前端 防抖&节流,你学到未啊?
    Promise实现一个函数,通过fetch请求一个接口'/api/getdata'(可能成功,也可能失败),超过3秒钟请求未返回则认为超时
    手写实现deepClone方法
    手写Promise.retry方法;实现次数内重试请求
    element-ui的table表格通过子表数据,进行展示左侧展开箭头
    页面导出为PDF格式
    js自定义数字跳动效果computeNumber
  • 原文地址:https://www.cnblogs.com/onglublog/p/16095147.html
Copyright © 2020-2023  润新知