Codeforces Round #111 (Div. 2)

 

A. Twins

题意就是问取最少的物品使价值严格大于总价值的一半。

排序就OK了。

#include <bits/stdc++.h>
using namespace std;
 
const int N = 1011;
int a[N];
 
int main() {
    int n;
    scanf("%d", &n);
    int sum = 0;
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
        sum += a[i];
    }
    sort(a, a + n, greater<int>());
    int temp = 0;
    for (int i = 0; i < n; i++) {
        temp += a[i];
        if (temp > sum / 2) {
            printf("%d\n", i + 1);
            break;
        }
    }
    return 0;
}
View Code

 

B. Unlucky Ticket

题面说了很多。其实就是,给一个长度为$2 \times N$的数组,问前一半和后一半存不存在一种双射使得前一半严格小于或严格大于后一半。

同排序即可。(然而忘了判断等于号的情况挂了两发...)

#include <bits/stdc++.h>
using namespace std;
 
const int N = 250;
char s[N];
 
int main() {
    int n;
    scanf("%d", &n);
    scanf("%s", s + 1);
    sort(s + 1, s + n + 1);
    sort(s + n + 1, s + 2 * n + 1);
    bool flag = false;
    if (s[1] < s[n + 1]) {
        for (int i = 2; i <= n; i++) {
            if (s[i] >= s[n + i]) flag = 1;
        }
    } else if (s[1] > s[n + 1]) {
        for (int i = 2; i <= n; i++) {
            if (s[i] <= s[n + i]) flag = 1;
        }
    } else {
        flag = 1;
    }
    if (flag) puts("NO");
    else puts("YES");
    return 0;
}    
View Code

 

C. Find Pair

题意很简单,把一个区间的任取两个数(可重复)组成一个pair,按字典序排序,问第$K$个是哪个。

刚开始又忘了相同数字的情况。又WA了一发。
把所有数字放进一个$map$里,那么每个数字$a$作为pair的第一个元素就有$map[a] \times n$个,然后扫一遍就行了。

#include <bits/stdc++.h>
using namespace std;
 
const int N = 1e5 + 7;
int n;
long long k;
int a[N];
map<int, int> mp;
 
int main() {
    cin >> n >> k;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        mp[a[i]]++;
    }
    sort(a + 1, a + n + 1);
    for (auto p: mp) {
        int now = p.first, cnt = p.second;
        if (k > 1LL * cnt * n) {
            k -= 1LL * cnt * n;
            continue;
        }
        cout << now << ' ' << a[(k + cnt - 1) / cnt] << endl;
        break;
    }
    return 0;
}
View Code

 

D. Edges in MST

其实我vp这一场是为了做E题的,但是没有想到D题居然是前几天在51nod做过的题...

给一个无向图,让你判断每一条边:
1. 不在任何MST中
2. 在一些MST中
3. 在所有MST中

考虑Kruskal求MST的过程,先按权值从小到大排序并用并查集维护点所处的连通块,然后枚举每一条边,如果这条边的两个点在同一个连通块中,那么这条边肯定不在任何MST中,那么第一种情况就解决了。
其他边肯定在某些MST中,现在只需考虑哪些边会在所有MST中。
会出现多个MST的情况是,加入这条边后,出现了环,并且环上最大权值的边有两条及以上,那么这些边就是可以互相替代的。当某条边不存在于权值小于等于它的边组成的环时,则说明这条边存在于所有MST中。这个定义就是无向图中的。所以每次把相同权值的边都建一次图,然后跑tarjan判桥就行了。

#include <bits/stdc++.h>
using namespace std;
 
const int N = 1e5 + 7;
struct Edge {
    int u, v, cost, id;
    bool operator < (const Edge &rhs) const {
        return cost < rhs.cost;
    }
} edge[N];
 
struct E {
    int v, ne, id;
} e[N << 1];
int low[N], dfn[N], id, head[N], cnt, ans[N];
 
inline void add(int u, int v, int id) {
    e[cnt].v = v; e[cnt].ne = head[u]; e[cnt].id = id; head[u] = cnt++;
    e[cnt].v = u; e[cnt].ne = head[v]; e[cnt].id = id; head[v] = cnt++;
}
 
int fa[N];
int getfa(int u) { return fa[u] == u ? u : fa[u] = getfa(fa[u]); }
 
void dfs(int u, int in_edge) {
    dfn[u] = low[u] = ++id;
    for (int i = head[u]; ~i; i = e[i].ne) {
        int v = e[i].v;
        if (!dfn[v]) {
            dfs(v, i);
            low[u] = min(low[u], low[v]);
            if (dfn[u] < low[v]) ans[e[i].id] = 0;
        } else if (i != (in_edge ^ 1)) {
            low[u] = min(low[u], dfn[v]);
        }
    }
}
 
void unite(int u, int v) {
    u = getfa(u), v = getfa(v);
    if (u != v) fa[u] = v;
}
 
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) fa[i] = i;
    for (int i = 0; i < m; i++) 
        scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].cost), edge[i].id = i;
    sort(edge, edge + m);
    for (int i = 0; i < m;) {
        int j = i;
        while (j + 1 < m && edge[j + 1].cost == edge[i].cost) j++;
        for (int k = i; k <= j; k++) {
            edge[k].u = getfa(edge[k].u);
            edge[k].v = getfa(edge[k].v);
            dfn[edge[k].u] = dfn[edge[k].v] = 0;
            head[edge[k].u] = head[edge[k].v] = -1;
            low[edge[k].u] = low[edge[k].v] = 0;
        }
        id = 0;
        cnt = 0;
        for (int k = i; k <= j; k++) {
            if (edge[k].u == edge[k].v) {
                ans[edge[k].id] = 2;
                continue;
            }
            ans[edge[k].id] = 1;
            add(edge[k].u, edge[k].v, edge[k].id);
        }
        for (int k = i; k <= j; k++) 
            if (!dfn[edge[k].u]) dfs(edge[k].u, -1);
        for (int k = i; k <= j; k++) {
            unite(edge[k].u, edge[k].v);
        }
        i = j + 1;
    }
    for (int i = 0; i < m; i++)
        if (ans[i] == 2) puts("none");
        else if (ans[i] == 1) puts("at least one");
        else puts("any");
    return 0;
}
View Code

 

E. Buses and People

很难受,赛中交上去WA 8,一直不知道错哪,赛后一看我的提交记录,诶,为啥后面跟着个⚠,一打开 out of bounds... 数组开大一点就过了。

有$n$辆公交,起点$s_i$,终点$f_i$,出发时间$t_i$。
$m$个人,起点$l_i$,终点$r_i$,出发时间$b_i$。
第$i$个人能坐上的车$j$需满足$s_j \leq l_i$,$r_i \leq f_j$,$b_i \leq t_j$,求每个人能坐上的车中出发时间最早的车的编号。

前几天做过一些题之后就觉得这样的题很套路了。
相当于在一个二维矩阵里RMQ,具体为二维矩阵第一维表示公交的起点坐标$s$,第二维表示公交的终点坐标$f$,矩阵里存的值为出发时间$t$的最小值(如果出现$s,f$都相同的情况则取$min$),对于每一个人的$l_i, r_i$,求矩阵第一维范围为$\left[1, l_i\right]$,第二维为$\left[r_i, inf\right]$的矩阵的最小的大于$b_i$的值。因为不带修改,那么我们可以扫描线消去一维。
把所有人和公交都按左端点排序,现在发现一个问题,如果线段树对应节点代表的是右端点的时间值的话,无法求出最小的大于$b_i$的值。
那么我们可以转化一下,线段树表示$t$时间能到达最远的右端点,那么当$t$相同时右端点取最大的肯定是最优的。查询就是查询线段树$t_i$到$inf$中最小的大于$r_i$的bus编号。
那么就优先查左子树,如果满足直接返回,不满足再查右子树。当区间最大值小于$r_i$返回$-1$进行剪枝。

#include <bits/stdc++.h>
using namespace std;
 
const int N = 1e6 + 7;
 
struct BUS {
    int s, f, t, id;
    inline bool operator < (const BUS &rhs) const {
        return s < rhs.s;
    }
} bus[N];
int temp[N * 4];
 
struct PEOPLE {
    int l, r, b, id;
    inline bool operator < (const PEOPLE &rhs) const {
        return l < rhs.l;
    }
} peo[N];
 
struct Seg {
    #define lp p << 1
    #define rp p << 1 | 1
    int pos[N << 2], mx[N << 2];
    inline void pushup(int p) {
        mx[p] = max(mx[lp], mx[rp]);
        pos[p] = mx[p] == mx[lp] ? pos[lp] : pos[rp];
    }
    void update(int p, int l, int r, int x, int val, int id) {
        if (l == r) {
            if (val >= mx[p]) {
                mx[p] = val;
                pos[p] = id;
            }
            return;
        }
        int mid = l + r >> 1;
        if (x <= mid) update(lp, l, mid, x, val, id);
        else update(rp, mid + 1, r, x, val, id);
        pushup(p);
    }
    int query(int p, int l, int r, int x, int y, int val) {
        if (mx[p] < val) return -1;
        if (x <= l && y >= r && mx[p] < val) return -1;
        if (l == r) {
            if (mx[p] >= val) return pos[p];
            return -1;
        }
        int mid = l + r >> 1;
        if (x <= mid) {
            int temp = query(lp, l, mid, x, y, val);
            if (temp != -1) return temp;
            return query(rp, mid + 1, r, x, y, val);
        }
        return query(rp, mid + 1, r, x, y, val);
    }
} seg;
 
int ans[N];
 
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    int cnt = 0;
    for (int i = 1; i <= n; i++) {
        scanf("%d%d%d", &bus[i].s, &bus[i].f, &bus[i].t);
        bus[i].id = i;
        temp[++cnt] = bus[i].t;
    }
    for (int i = 1; i <= m; i++) {
        scanf("%d%d%d", &peo[i].l, &peo[i].r, &peo[i].b);
        peo[i].id = i;
        temp[++cnt] = peo[i].b;
    }
    sort(temp + 1, temp + 1 + cnt);
    cnt = unique(temp + 1, temp + 1 + cnt) - temp - 1;
    for (int i = 1; i <= n; i++) {
        bus[i].t = lower_bound(temp + 1, temp + 1 + cnt, bus[i].t) - temp;
    }
    for (int i = 1; i <= m; i++) {
        peo[i].b = lower_bound(temp + 1, temp + 1 + cnt, peo[i].b) - temp;
    }
    sort(bus + 1, bus + 1 + n);
    sort(peo + 1, peo + 1 + m);
    int now = 1;
    for (int i = 1; i <= m; i++) {
        while (now <= n && bus[now].s <= peo[i].l) {
            seg.update(1, 1, cnt, bus[now].t, bus[now].f, bus[now].id);
            now++;
        }
        ans[peo[i].id] = seg.query(1, 1, cnt, peo[i].b, cnt, peo[i].r);
    }
    for (int i = 1; i <= m; i++)
        printf("%d%c", ans[i], " \n"[i == m]);
    return 0;
}
View Code

 

posted @ 2019-10-14 18:40  Mrzdtz220  阅读(184)  评论(0编辑  收藏  举报