【解题总结】Waterloo Local 2012-2013(Codeforces Gym 100169)

感官难度: C = D < A < E < B C=D<A<E<B

A Bridges and Tunnels

题意:给定一张无向图,图上边分为室外和室内两类,每条边有权值。求以室外的边权和为第一关键字,室内和室外的边权和为第二关键字的最短路。

直接用 pair 存一下这两个关键字即可。

#include <bits/stdc++.h>
#define INF 10000000000000000ll
using namespace std;
typedef long long ll;
typedef pair<int, pair<ll, ll> > pill;

int n, m, p;
int to[80005], nxt[80005], tp[80005];
int w[80005], at[4005] = {0}, cnt = 0;
pair<ll, ll> dis[4005];
priority_queue<pill, vector<pill >, greater<pill > > pq;
int main(){
    scanf("%d%d%d", &n, &m, &p);
    char opt[3];
    for (int i = 0, u, v, ww; i < m; ++i){
        scanf("%d%d%d%s", &u, &v, &ww, opt);
        int tpp = (opt[0] == 'I' ? 0: 1);
        to[++cnt] = v, nxt[cnt] = at[u], w[cnt] = ww, tp[cnt] = tpp, 
        at[u] = cnt;
        to[++cnt] = u, nxt[cnt] = at[v], w[cnt] = ww, tp[cnt] = tpp, 
        at[v] = cnt;
    }
    while (p--){
        int u, v;
        scanf("%d%d", &u, &v);
        
        for (int i = 0; i < n; ++i)
            dis[i].first = dis[i].second = INF;
        dis[u].first = dis[u].second = 0;
        pq.push(make_pair(u, dis[u]));
        for (; ; ){
            while (!pq.empty()){
                if (pq.top().second > dis[pq.top().first])
                    pq.pop();
                else break;
            }
            if (pq.empty()) break;
            pill tmp = pq.top();
            pq.pop();
            int h = tmp.first;
            ll dis_o = tmp.second.first, dis_tot = tmp.second.second;
            for (int i = at[h]; i; i = nxt[i]){
                if (tp[i]){
                    pair<ll, ll> tmp_dis(dis_o + w[i], dis_tot + w[i]);
                    if (tmp_dis < dis[to[i]]){
                        dis[to[i]] = tmp_dis;
                        pq.push(make_pair(to[i], tmp_dis));
                    }
                }else {
                    pair<ll, ll> tmp_dis(dis_o, dis_tot + w[i]);
                    if (tmp_dis < dis[to[i]]){
                        dis[to[i]] = tmp_dis;
                        pq.push(make_pair(to[i], tmp_dis));
                    }
                }
            }
        }
        if (dis[v].first < INF) printf("%lld %lld\n", dis[v].first, dis[v].second);
        else printf("IMPOSSIBLE\n");
    }
    return 0;
}

E Tetrahedron Inequality

题意:给 6 条边,问能否组成一个体积非 0 的正四面体。多组数据。

枚举 6 条边的所有排列。首先保证能组成四个三角形,然后判断体积是否为 0。体积为 0 的情况只有一种:6 条边构成了平面图形。

这个不是很好判断。我的做法是固定一个底面,然后让其中一个侧面旋转到与底面共面,这时有两种情况:侧面与底面在公共边的两侧或者一侧。计算两种情况下侧面和底面各自非公共点之间的距离,记为 l 1 , l 2 l_1, l_2

如果剩下的唯一没有用到的边长度在 l 1 , l 2 l_1, l_2 之间就表明可以形成。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int n;
int l[10];
inline bool check(int x, int y, int z){
    return (x + y > z) && (x + z > y) && (y + z > x);
}
inline ll sqr(ll x){
    return x * x;
}
inline double sqr_d(double x){
    return x * x;
}
int main(){
    scanf("%d", &n);
    while (n--){
        for (int i = 0; i < 6; ++i)
            scanf("%d", &l[i]);
        sort(l, l + 6);
        bool flag = false;
        do{
            if (!check(l[0], l[1], l[4]) || !check(l[3], l[4], l[5]) || !check(l[1], l[2], l[5])
                || !check(l[0], l[2], l[3])){
                continue;
            }
            
            double cos_1 = (1.0 * sqr(l[3]) + 1.0 * sqr(l[5]) - 1.0 * sqr(l[4])) / (2.0 * l[3] * l[5]);
            double sin_1 = sqrt(1 - cos_1 * cos_1);  // bad in precision
            double xx1 = l[3] * cos_1, yy1 = l[3] * sin_1;

            double cos_2 = (1.0 * sqr(l[2]) + 1.0 * sqr(l[5]) - 1.0 * sqr(l[1])) / (2.0 * l[2] * l[5]);
            double sin_2 = sqrt(1 - cos_2 * cos_2);  // bad in precision
            double xx2 = l[2] * cos_2, yy2 = l[2] * sin_2;

            double dis1 = sqrt(sqr_d(xx1 - xx2) + sqr_d(yy1 - yy2));
            double dis2 = sqrt(sqr_d(xx1 - xx2) + sqr_d(yy1 + yy2));
            if ((l[0] - dis1) * (l[0] - dis2) < 0) {
                flag = true;
                break;
            }
        }while (next_permutation(l, l + 6));
        if (flag) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

B Secret Polynomial

题意:有一个未知的非负系数多项式 f f ,给定 f ( 1 ) , f ( f ( 1 ) ) f(1), f(f(1)) ,问是否能算出 f f 的各个系数,或者判别无解或者多解。

假设 f ( 1 ) = t f(1)=t 。由于系数非负,我们可以立即特判几种情况:

  1. t < 0 t<0 。这时必然无解。
  2. t < f ( f ( 1 ) ) t<f(f(1)) 。由单调性,此时无解。
  3. t = 0 t=0 f ( f ( 1 ) ) = 0 f(f(1))=0 时有唯一解,否则无解。
  4. t = f ( f ( 1 ) ) t=f(f(1)) f ( f ( 1 ) ) > 1 f(f(1))>1 时有唯一解,否则多解,如 f ( x ) = x f(x) = x f ( x ) = 1 f(x)=1
  5. t = 1 t=1 。如果未在情况 4 中被检测就无解。

然后就只有无解和唯一解的情况了。会发现此时 t 2 t\ge 2 ,且各个系数均 t \le t 。我们可以从低次项到高次项,迭代计算系数:

  1. b = f ( f ( 1 ) ) b = f(f(1)) ,当前次数 d = 0 d=0
  2. 如果 b < t b < t ,那么 a d = b a_d = b ,退出迭代。
  3. 如果 b = t b = t ,那么 a d = t a_d = t 或者 a d = 0 , a d + 1 = 1 a_d = 0, a_{d+1} = 1 ,判断哪种更合理后退出迭代。
  4. 如果 b > t b > t ,那么 a d = b m o d t a_d = b \bmod t ,此时取 b : = b t , d : = d + 1 b := \lfloor \frac{b}{t}\rfloor, d := d + 1 ,回到 2。

上述求解过程不保证最终的系数和为 t t ,因此最后要判定一下,不为 t t 则无解。

#include <bits/stdc++.h>
using namespace std;

int ans[1005], tot;
int main(){
    int n;
    scanf("%d", &n);
    while (n--){
        int a, b;
        scanf("%d%d", &a, &b);
        if (a < 0 || b < 0) {
            printf("IMPOSSIBLE\n");
            continue;
        }
        if (b < a){
            printf("IMPOSSIBLE\n");
            continue;
        }
        if (a == 0){
            // f(x) = 0
            if (b != 0) printf("IMPOSSIBLE\n");
            else printf("0\n");
            continue;
        }
        if (a == b){
            // f(x) = x, or f(x) = c
            if (a == 1) printf("AMBIGUOUS\n");
            else printf("0\n");
            continue;
        }
        if (a == 1){
            // should be a = b
            printf("IMPOSSIBLE\n");
            continue;
        }

        // a >= 2
        tot = 0;
        int cur = 0;
        for (; ; ){
            if (b < a) {
                ans[++tot] = b;
                cur += b;
                break;
            }else if (b == a){
                if (a - cur == 1){
                    ans[++tot] = 0;
                    ans[++tot] = 1;
                    ++cur;
                }else {
                    ans[++tot] = a;
                    cur += a;
                }
                break;
            }else {
                ans[++tot] = b % a;
                cur += ans[tot];
                b /= a;
            }
        }
        if (cur != a){
            printf("IMPOSSIBLE\n");
            continue;
        }
        for (int i = tot; i >= 2; --i)
            printf("%d ", ans[i]);
        printf("%d\n", ans[1]);
    }    
    return 0;
}

小结

注意特判!注意特判!注意特判!

这场比赛的题目都不难,但是很容易掉进坑里,特别是 B 和 E!

猜你喜欢

转载自blog.csdn.net/zqy1018/article/details/108005163