Dual-SIM Phone

题意

n 个运营商,每个运营商可以发送短信给其他的一些运营商。彼得可以选两个运营商,通过两个运营商中花钱更少的一个发送短信给运营商。帮助彼得选择一对运营商,使得他能够发送短信给所有运营商,而且发送短信的最大费用最小。

n104
运营商能发送短信的关系数 k105
Time Limits:2000ms
Memory Limits:64M

分析

这题其实就是暴力优化。
考虑二分答案。用 bitset 记录一个运营商 i 在当前答案限制下能发给的运营商的集合 Si ,并记录集合大小 Ci 。然后将 C 数组从大到小排序,枚举两个运营商 i,j ,显然,当 nCi+Cj i,j 才可能满足条件,所以我们做到一个 Ci+Cj<n 时就可 break 。考虑到 k 只是 n 的十倍,这会是个非常高效的剪枝。

需要注意的是最好少使用 bitset 的函数。

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <bitset>
using namespace std;

const int N = 1e4 + 10;
int g[N],next[N * 10],to[N * 10],d[N * 10];
int n,m,c[N],a[N],tot;
bitset<N> s[N],cur,full; 

void add(int x,int y,int z) {
    to[++ tot] = y;
    next[tot] = g[x];
    g[x] = tot;
    d[tot] = z;
}

bool cmp(int a,int b) {
    return c[a] > c[b];
}

bool check(int lim) {
    for (int i = 1;i <= n;i ++) s[i].reset();
    for (int u = 1;u <= n;u ++) {
        c[u] = 0;
        a[u] = u;
        for (int i = g[u];i;i = next[i]) if (d[i] <= lim) {
            int v = to[i];
            if (s[u][v]) continue;
            s[u][v] = 1;
            c[u] ++;
        }
    }
    sort(a + 1,a + 1 + n,cmp);
    for (int i = 1;i < n;i ++) {
        for (int j = i + 1;j <= n;j ++) if (c[a[i]] + c[a[j]] < n) break;
        else {
            cur = s[a[i]] | s[a[j]];
            if (cur == full) return true;
        }
    }
    return false;
}

int main() {
    scanf("%d%d",&n,&m);
    for (int i = 1;i <= m;i ++) {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
    }
    for (int i = 1;i <= n;i ++) full[i] = 1;
    int ans = 1,r = 1e9;
    while (ans < r) {
        int mid = (ans + r) >> 1;
        if (check(mid)) r = mid;
        else ans = mid + 1;
    }
    if (check(ans)) printf("%d",ans);
    else printf("No solution");
}

你可能感兴趣的:(Dual-SIM Phone)