AtCoder Regular Contest 092(部分)

C:2D Plane 2N Points

题意:红点和蓝点之间满足一个偏序关系那么就可以配对,但是蓝点最多只能和一个红点配对,每个红点也最多只能和一个蓝点配对,问最多能陪多少对


思路:可以配对的红点蓝点连一条边,二分图匹配一下就行了

#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 150;
using namespace std;

int use[maxn], from[maxn];
vector<int> G[maxn];
int n, k;

int dfs(int x) {
    for(int i = 0; i < G[x].size(); i++) {
        int u = G[x][i];
        if(!use[u]) {
            use[u] = 1;
            if(from[u] == -1 || dfs(from[u])) {
                from[u] = x;
                return 1;
            }
        }
    }
    return 0;
}

int maxp() {
    memset(from, -1, sizeof(from));
    int sum = 0;
    for(int i = 1; i <= n; i++) {
        memset(use, 0, sizeof(use));
        sum += dfs(i);
    }
    return sum;
}

int a[maxn], b[maxn], c[maxn], d[maxn];

int main() {
    while(scanf("%d", &n) != EOF) {
        for(int i = 0; i < maxn; i++) G[i].clear();
        for(int i = 1; i <= n; i++) scanf("%d %d", &a[i], &b[i]);
        for(int i = 1; i <= n; i++) {
            scanf("%d %d", &c[i], &d[i]);
            for(int j = 1; j <= n; j++) {
                if(c[i] > a[j] && d[i] > b[j]) G[i].push_back(j);
            }

        }
        int ans = maxp();
        cout << ans << endl;
    }
    return 0;
}

D:Two Sequences

题意:N个数的a数组和b数组,对所有的ai + bj的异或和是多少

思路:可以统计每一个位上出现的1的数量,如果为奇数,那么那上面就是1,否则就是0,从低位往高位看起,看到第x位的时候,对b数组做处理,对每个数
记录bi / (1 << x)的最低位pi(只有0或者1), 还有bi % (1 << x)的值qi,按pi排序,那么考虑ai的时候,也类似计算出Pi, Qi,ai对x这个位上的1的贡献可以
这样算:
如果Pi = 0, 那么对b数组中能在x位上产生1的情况是这样的:
(1) pj = 0, (qj + Qi) >= (1 << x) (即产生了进位)

(2) pj = 1, (qj + Qi) < (1 << x) (即不产生进位)

#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 2e5 + 10;
using namespace std;

typedef pair<ll, ll> pa;
int n, k;
ll a[maxn], b[maxn], c[maxn], d[maxn];
pa res[maxn];

int main() {
    while(scanf("%d", &n) != EOF) {
        for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
        for(int j = 1; j <= n; j++) scanf("%lld", &b[j]);
        sort(a + 1, a + n + 1);
        sort(b + 1, b + n + 1);
        ll ans = 0;
        for(ll i = 0; i <= 30; i++) {
            ll x = (ll)1 << i;
            for(ll j = 1; j <= n; j++) {
                res[j - 1].first = (b[j] / x) & 1;
                res[j - 1].second = (b[j] % x);
            }
            sort(res, res + n);
            int id = 0;
            ll tot = 0;
            for(int ii = 0; ii < n; ii++) if(!res[ii].first) id = ii + 1;
            for(ll j = 1; j <= n; j++) {
                ll nx = (a[j] / x) & 1;
                ll ny = a[j] % x;
                if(nx == 0) {
                    ///为0要进位
                    ll c = (lower_bound(res, res + id, pa(0, x - ny)) - res);
                    tot += id - c;
                    ///为1不要进位
                    tot += lower_bound(res + id, res + n, pa(1, x - ny)) - (res + id);
                } else { ///不需要进位就有1
                    ///为0不需要进位
                    tot += lower_bound(res, res + id, pa(0, x - ny)) - res;
                    ///为1需要进位
                    ll ix = lower_bound(res + id, res +n, pa(1, x - ny)) - (res +id);
                    tot += n - (lower_bound(res + id, res + n, pa(1, x - ny)) - res);
                }
                //cout << "tot = " << tot << endl;
            }
            //cout << "tot = " << tot << endl;
            if(tot & 1) ans = ans | x;
        }
        cout << ans << endl;
    }
    return 0;
}

F:Two Faced Edges

题意:n个点m条边的有向图,对于每条边,如果改变它的方向,是否会改变原来图中的强连通分量的情况

思路:对于u -> v这条边,改变它的方向时,有以下两种情况
1.u, v属于相同的强连通分量时,那么需要保证原来u -> v的所有路径中在删除u->v这条边后还至少有一条路径可以从u到达v
2.u, v属于不同的强连通分量时,那么需要保证原来u -> v的所有路径中在删除u->v这条边后不存在一条路径可以从u到达v
也就是说, 对于情况1,如果之前v到u可达,那么改变边之后u还要可达v
对于情况2,如果之前v到u不可达,那么改变边之后u不能到达v

每一种情况的前者好求,dfs一遍就可以了,后者用深度优先搜索对邻接的边从前往后搜索一遍, 再从后往前搜索一遍, 看到达一个点的最先遍历到的边是否一样,如果一样那么就是只有一条路径可达, 否则就是有多条路径可达

#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 1e3 + 5;
const int INF = 1e9 + 10;
using namespace std;

struct P {
    int to, flag, id;
    P() {}
    P(int t, int f, int i) : to(t), flag(f), id(i) {}
};
int n, m, T, kase = 1;
vector<P> G[maxn];
bool can[maxn][maxn];
int min_flag[maxn], max_flag[maxn];
int ans[200 * maxn];

bool can_arrive(int x) {
    can[x][x] = true;
    queue<int> que; que.push(x);
    while(!que.empty()) {
        int u = que.front(); que.pop();
        for(int i = 0; i < G[u].size(); i++) {
            int to = G[u][i].to;
            if(can[x][to]) continue;
            can[x][to] = true; que.push(to);
        }
    }
}

void dfs(int x, int t, int flag) {
    if(flag == 0 && min_flag[x] != INF) return ;
    if(flag == 1 && max_flag[x] != -INF) return ;
    if(flag == 0) min_flag[x] = min(min_flag[x], t);
    if(flag == 1) max_flag[x] = max(max_flag[x], t);
    for(int i = 0; i < G[x].size(); i++) {
        int to = G[x][i].to;
        dfs(to, t, flag);
    }
}

void solve(int x) {
    for(int i = 1; i <= n; i++) {
        min_flag[i] = INF;
        max_flag[i] = -INF;
    }
    min_flag[x] = max_flag[x] = 0;
    for(int i = 0; i < G[x].size(); i++) {
        int to = G[x][i].to;
        dfs(to, G[x][i].flag, 0);
    }
    for(int i = G[x].size() - 1; i >= 0; i--) {
        int to = G[x][i].to;
        dfs(to, G[x][i].flag, 1);
    }
    for(int i = 0; i < G[x].size(); i++) {
        int flag = 1, to = G[x][i].to;
        if(min_flag[to] == max_flag[to]) flag = 0;
        if(can[to][x] != flag) flag = 0;
        else flag = 1;
        ans[G[x][i].id] = flag;
    }
}


int main() {
    while(scanf("%d %d", &n, &m) != EOF) {
        for(int i = 0; i < maxn; i++) G[i].clear();
        for(int i = 1; i <= m; i++) {
            int u, v, sz;
            scanf("%d %d", &u, &v);
            sz = G[u].size() + 1;
            G[u].push_back(P(v, sz, i));
        }
        memset(can, false, sizeof can);
        for(int i = 1; i <= n; i++) can_arrive(i);
        for(int i = 1; i <= n; i++) solve(i);
        for(int i = 1; i <= m; i++) printf("%s\n", ans[i] ? "same" : "diff");
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值