• AtCoder Regular Contest 092 C D E F


    C - 2D Plane 2N Points

    题意

    二维平面上有(N)个红点,(N)个蓝点,一个红点和一个蓝点能配成一对当且仅当(x_r<x_b)(y_r<y_b).

    问最多能形成多少pair.

    思路

    无脑版本:可以匹配的连边,然后跑匈牙利。

    正确的贪心姿势:对于所有的点按(x)从小到大排序,对于蓝点,要匹配的最优的红点即为 在其之前出现的 (y)小于它的 且(y)最大的 红点。用一个(set)维护红点的(y)坐标即可。

    Code

    #include <bits/stdc++.h>
    #define F(i, a, b) for (int i = (a); i < (b); ++i)
    #define F2(i, a, b) for (int i = (a); i <= (b); ++i)
    #define dF(i, a, b) for (int i = (a); i > (b); --i)
    #define dF2(i, a, b) for (int i = (a); i >= (b); --i)
    #define maxn 110
    using namespace std;
    typedef long long LL;
    struct Point {
        int x,y,type;
        bool operator < (const Point& p) const { return x < p.x; }
    }a[maxn<<1];
    set<int> st;
    int main() {
        int n;
        scanf("%d", &n);
        F(i, 0, n) scanf("%d%d", &a[i].x, &a[i].y), a[i].type=0;
        F(i, 0, n) scanf("%d%d", &a[n+i], &a[n+i].y), a[n+i].type=1;
        sort(a, a+(n<<1));
        int ans=0;
        F(i, 0, n<<1) {
            if (a[i].type) {
                auto it = st.lower_bound(a[i].y);
                if (it==st.begin()) continue;
                else st.erase(--it), ++ans;
            }
            else {
                st.insert(a[i].y);
            }
        }
        printf("%d
    ", ans);
        return 0;
    }
    
    

    D - Two Sequences

    题意

    两个序列(A,B),对所有的(A_i+B_j)求异或和。

    思路

    按位考虑,不妨考虑第(k)位。

    怎么才能知道第(k)位是(0)还是(1)呢?
    即考虑所有的和当中有奇数个还是偶数个第(k)位是(1)

    对于某个和,怎么知道其第(k)位是(0)还是(1)呢?
    注意到,高于(k)位的都不会产生影响,因此,只需要考虑(a_i\%2^{k+1})(b_i\%2^{k+1})的和。若该和(s)满足(2^kleq slt 2cdot2^k)或者(3cdot2^kleq slt 4cdot2^k),则该位为(1).

    所以对于每个(a_i\%k),将(b_i\%k)排序,二分即可。

    Code

    #include <bits/stdc++.h>
    #define F(i, a, b) for (int i = (a); i < (b); ++i)
    #define F2(i, a, b) for (int i = (a); i <= (b); ++i)
    #define dF(i, a, b) for (int i = (a); i > (b); --i)
    #define dF2(i, a, b) for (int i = (a); i >= (b); --i)
    #define maxn 200010
    using namespace std;
    typedef long long LL;
    int a[maxn], b[maxn], bb[maxn];
    int main() {
        int n;
        scanf("%d", &n);
        F(i, 0, n) scanf("%d", &a[i]);
        F(i, 0, n) scanf("%d", &b[i]);
        int mod=1, ans=0;
        F(k, 0, 29) {
            F(i, 0, n) bb[i] = b[i] % (mod<<1);
            sort(bb, bb+n);
            int cnt=0;
            F(i, 0, n) {
                int temp=a[i]%(mod<<1);
                int p1=lower_bound(bb, bb+n, mod-temp)-bb,
                    p2=lower_bound(bb, bb+n, (mod<<1)-temp)-bb,
                    p3=lower_bound(bb, bb+n, mod*3-temp)-bb,
                    p4=lower_bound(bb, bb+n, (mod<<2)-temp)-bb;
                cnt += p2-p1+p4-p3;
            }
            ans += cnt&1 ? mod : 0;
            mod <<= 1;
        }
        printf("%d
    ", ans);
        return 0;
    }
    
    

    E - Both Sides Merger

    题意

    对一个序列能够进行两种操作:

    1. 选取开头或结尾的元素,直接删去;
    2. 选取中间的某个元素,用它两旁的元素之和代替它,再去掉两旁的两个元素。

    现要求经过若干次操作,只剩下一个元素,且值最大,输出操作。

    思路

    不难发现,所有选取的元素在原序列中的奇偶性相同;
    反过来,只要所有元素在原序列中奇偶性相同,即可构造出相应的操作序列。

    只取正数很容易就能得到最优取法。
    // 注意特判全为负数的情况

    现在考虑一个取法(d_0,d_1,ldots,d_k),首先将开头(lt d_0)和结尾(gt d_k)的一系列元素删去,接下来考虑对中间的元素进行的操作。

    对于(d_0)(d_1)一段,因为奇偶性相同,所以中间必然夹着奇数个元素,每次取最中间的即可将序列不断地往中间缩,直至(d_0)(d_1)合并。接下来再作为一个整体与(d_2)合并,依次类推。

    Code

    #include <bits/stdc++.h>
    #define F(i, a, b) for (int i = (a); i < (b); ++i)
    #define F2(i, a, b) for (int i = (a); i <= (b); ++i)
    #define dF(i, a, b) for (int i = (a); i > (b); --i)
    #define dF2(i, a, b) for (int i = (a); i >= (b); --i)
    #define maxn 1010
    using namespace std;
    typedef long long LL;
    int d1[maxn], d2[maxn], tot1, tot2, tot, n, a[maxn];
    int* d;
    vector<int> ans;
    void solve() {
        F(i, 1, d[0]) ans.push_back(1);
        dF(i, n, d[tot-1]) ans.push_back(i-d[0]+1);
        F2(i, 1, tot-1) {
            int lim=(d[i]-d[i-1])>>1;
            dF(j, 1+lim, 1) ans.push_back(j);
        }
    }
    int main() {
        scanf("%d", &n);
        int p=1;
        F2(i, 1 ,n) {
            scanf("%d", &a[i]);
            p = a[i] > a[p] ? i : p;
        }
        LL sum;
        if (a[p]<=0) d1[tot++]=p, sum=a[p], d=d1;
        else {
            LL sum1=0, sum2=0;
            for (int i = 1; i <= n; i += 2) if (a[i]>0) d1[tot1++]=i, sum1+=a[i];
            for (int i = 2; i <= n; i += 2) if (a[i]>0) d2[tot2++]=i, sum2+=a[i];
            d = sum1 < sum2 ? d2 : d1;
            tot = sum1 < sum2 ? tot2 : tot1;
            sum = max(sum1, sum2);
        }
        solve();
        printf("%lld
    %d
    ", sum, ans.size());
        for (auto x : ans) printf("%d
    ", x);
        return 0;
    }
    
    

    F - Two Faced Edges

    题意

    给定一个有向图,每次将一条边方向反转,问这个操作是否改变了图中的强连通分量数,

    思路

    结论
    假设原有边(a_i ightarrow b_i),考虑接下来两个命题:
    1, 原图中能从(b_i)走到(a_i)
    2. 原图中删去了(a_i ightarrow b_i)边后仍能从(a_i)走到(b_i)
    则图中的强连通分量数不改变 当且仅当 命题1与命题2同真假。

    下面考虑命题2如何(check).
    考虑将从(a_i)出去的边标号,先沿着边的标号从小到大进行(dfs),对图中每个点的标记则为其第一次被访问是来自哪条边的行为;再从大到小进行(dfs),对图中每个点的标记则为其最后一次被访问是来自哪条边的行为。
    // 两个标记即相当于前缀与后缀的作用
    如果这两个标记不都等于删去的边的标号(若都等于,则意味着该边为必经边),则删去该边仍能走到。

    至于命题1,可以建立在命题2的基础上,也可以直接每个点一次(dfs).

    Code

    #include <bits/stdc++.h>
    #define F(i, a, b) for (int i = (a); i < (b); ++i)
    #define F2(i, a, b) for (int i = (a); i <= (b); ++i)
    #define dF(i, a, b) for (int i = (a); i > (b); --i)
    #define dF2(i, a, b) for (int i = (a); i >= (b); --i)
    #define maxn 1010
    #define maxm 200010
    using namespace std;
    typedef long long LL;
    int f[maxn][maxn][2], vis[maxn<<1], T;
    struct node {int to, id; };
    vector<node> edge[maxn];
    pair<int, int> E[maxm];
    void dfs(int u, int src, int id, int type) {
        f[src][u][type] = id; vis[u] = T;
        for (auto& e : edge[u]) {
            int v = e.to;
            if (vis[v]!=T) dfs(v, src, id, type);
        }
    }
    void work(int src) {
        ++T; vis[src] = T;
        for (auto& e : edge[src]) {
            int v = e.to;
            if (vis[v]!=T) dfs(v, src, e.id, 0);
        }
    
        reverse(edge[src].begin(), edge[src].end());
        ++T; vis[src] = T;
        for (auto& e : edge[src]) {
            int v = e.to;
            if (vis[v]!=T) dfs(v, src, e.id, 1);
        }
    }
    int main() {
        int n, m, u, v;
        scanf("%d%d", &n ,&m);
        F2(i, 1, m) {
            scanf("%d%d", &u, &v);
            edge[u].push_back({v, i});
            E[i] = {u, v};
        }
        F2(i, 1, n) work(i);
        F2(i, 1, m) {
            int u = E[i].first, v = E[i].second;
            if (f[v][u][0]!=0 ^ (f[u][v][0]!=i||f[u][v][1]!=i)) puts("diff");
            else puts("same");
        }
        return 0;
    }
    
    
  • 相关阅读:
    2013年春季献礼 微软认证考试5.5折优惠,截止到5月25日
    邮件服务器脱离域灾难恢复
    How to: Hide the Ribbon in SharePoint 2010
    sharepoint2010如何根据用户登录名获取有权限的列表记录?
    sharepoint 2010的版本比较
    SharePoint2010文档归档策略(2)从放置库转移到自己定义的文档库
    如何设置sharepoint的栏目不在新增或修改页面显示?
    如何删除 SharePoint Workspace 2010 中的临时数据和永久数据
    Windows XP 默认蓝色桌面的 RGB
    System.Net.Socket Tcp 学习笔记(一)
  • 原文地址:https://www.cnblogs.com/kkkkahlua/p/8642655.html
Copyright © 2020-2023  润新知