UKIEPC 2017

A-Alien Sunset

这到题,用一个数组表示当前时间有多少个星球是夜晚,这样就转换成了区间修改单点查询。因为只查询一次,所以用差分即可。

#include <bits/stdc++.h>

using namespace std;

#define int long long

const int m = 1825 * 100 + 5;

int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    cin >> n;
    vector<int> a(m);
    for (int i = 1, h, l, r; i <= n; i++) {
        cin >> h >> r >> l;
        if (l > r) a[0]++, a[r + 1]--, r += h;
        while (r < m) {
            a[l]++;
            if (r + 1 < m) a[r + 1]--;
            l += h , r += h;
        }
    }
    for( int i = 1 ; i < m ; i ++ )
        a[i] += a[i-1];
    for( int i = 0 ; i < m ; i ++){
        if( a[i] == n ){
            cout << i << "\n";
            return 0;
        }
    }
    cout << "impossible\n";
    return 0;
}

B-Breaking Biscuits

这题可以理解为用两条平行板去夹一个多边形。

枚举任意两个点,连成一条线后,让这条直线与板平行,所有要计算出其他点到这个点的距离最大值,要用叉积去判断点在直线的哪一侧,因为两侧的距离要分别统计。

#include <bits/stdc++.h>

using namespace std;

#define int long long

typedef long double db;

const db inf = 1e9;

struct Point {// 点
    db x, y;

    Point(db x = 0, db y = 0) : x(x), y(y) {};
};

using Vec = Point;// 向量
struct Line { // 直线 (点向式)
    Point P;
    Vec v;

    Line(Point P, Vec v) : P(P), v(v) {};
};

struct Seg { // 线段(存两个端点)
    Point A, B;

    Seg(Point A, Point B) : A(A), B(B) {};
};

using Vec = Point;// 向量

bool operator==(Point A, Point B) {
    return A.x == B.x and A.y == B.y;
}

Vec operator+(Vec u, Vec v) { return Vec(u.x + v.x, u.y + v.y); } // 向量加法
Vec operator-(Vec u, Vec v) { return Vec(u.x - v.x, u.y - v.y); } // 向量减法
db operator^(Vec u, Vec v) { return u.x * v.y - u.y * v.x; }// 叉乘

db len(Vec v) { return sqrt(v.x * v.x + v.y * v.y); }// 向量长度

Line line(Seg l) { return Line(l.A, l.B - l.A); }

Point rotate(Point P, db rad) {
    return Point(cos(rad) * P.x - sin(rad) * P.y, sin(rad) * P.x + cos(rad) * P.y);
}

// 点到直线的距离 depends V^V, len
db dis(Point P, Line l) { return abs((P ^ l.v) - (l.P ^ l.v)) / len(l.v); }


int32_t main() {
    ios::sync_with_stdio(0), cin.tie(0);
    int n;
    cin >> n;
    vector<Point> P(n);
    for (auto &[x, y]: P) cin >> x >> y;
    db res = inf;
    for (auto i: P)
        for (auto j: P) {
            if (i == j) continue;
            auto l = line(Seg(i, j));
            db a = 0, b = 0;
            for (auto k: P) {
                auto d = dis(k, l);
                if (((k - l.P) ^ l.v) > 0) a = max(a, d);
                else b = max(b, d);
            }
            res = min(res, a + b);
        }
    cout << fixed << setprecision(10) << res << "\n";
    return 0;
}

这里再说另一种思路吧,依旧是两块平行板去夹多边形,考虑让两块板始终与\(y\)轴平行,这样的话\(x\)的最大最小值之差就是平行板的距离。所以我们要不断的旋转多边形,选择多边形实际上就是选择每一个顶点即可。旋转的复杂度可以认为是\(O(1)\),设选择\(t\)次,每次只要旋转\(\frac{\pi}{t}\)度就可以了,总共旋转半周就够用了。所以复杂度就是\(O(Nt)\),所以为了保证精度\(t\)越大越好。一般情况下我们会取\(t=1e6\),但是这题的取\(1e6\)就会把卡精度,本人亲测\(t=1.5e6\)即可通过。

#include <bits/stdc++.h>

using namespace std;

#define int long long

typedef long double db;

const db inf = 1e9;

struct Point {// 点
    db x, y;

    Point(db x = 0, db y = 0) : x(x), y(y) {};
};

using Vec = Point;// 向量
struct Line { // 直线 (点向式)
    Point P;
    Vec v;

    Line(Point P, Vec v) : P(P), v(v) {};
};

struct Seg { // 线段(存两个端点)
    Point A, B;

    Seg(Point A, Point B) : A(A), B(B) {};
};

using Vec = Point;// 向量

bool operator==(Point A, Point B) {
    return A.x == B.x and A.y == B.y;
}

Vec operator+(Vec u, Vec v) { return Vec(u.x + v.x, u.y + v.y); } // 向量加法
Vec operator-(Vec u, Vec v) { return Vec(u.x - v.x, u.y - v.y); } // 向量减法
db operator^(Vec u, Vec v) { return u.x * v.y - u.y * v.x; }// 叉乘

db len(Vec v) { return sqrt(v.x * v.x + v.y * v.y); }// 向量长度

Line line(Seg l) { return Line(l.A, l.B - l.A); }

Point rotate(Point P, db rad) {
    return Point(cos(rad) * P.x - sin(rad) * P.y, sin(rad) * P.x + cos(rad) * P.y);
}

// 点到直线的距离 depends V^V, len
db dis(Point P, Line l) { return abs((P ^ l.v) - (l.P ^ l.v)) / len(l.v); }

const int t = 1.5e6;
const db T = acos(-1) / t;

int32_t main() {
    ios::sync_with_stdio(0), cin.tie(0);
    int n;
    cin >> n;
    vector<Point> P(n);
    for (auto &[x, y]: P) cin >> x >> y;
    db res = inf;
    for (int i = t; i; i--) {
        db l = inf, r = -inf;
        for (const auto &[x, y]: P)
            l = min(l, x), r = max(r, x);
        res = min(res, r - l);
        for (auto &i: P)
            i = rotate(i, T);
    }
    cout << fixed << setprecision(10) << res << "\n";
    return 0;
}

C-Cued In

其实算是贪心吧?我们要先优先打分数最高,并且反复打分数最高,直到把红球用完,剩下的球随便打就行。

#include <bits/stdc++.h>

using namespace std;

#define int long long

int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    map<string, int> ct;
    ct["red"] = 1;
    ct["yellow"] = 2;
    ct["green"] = 3;
    ct["brown"] = 4;
    ct["blue"] = 5;
    ct["pink"] = 6;
    ct["black"] = 7;
    int n;
    cin >> n;
    vector<int> cnt(10);
    for (int i = 1; i <= n; i++) {
        string ball;
        cin >> ball;
        cnt[ ct[ball] ] ++;
    }
    int f = 0;
    for( int i = 1 ; i <= 7 ; i ++ )
        if( cnt[i] > 0 ) f = i;
    if( f == 1 ) {
        cout << "1";
        return 0;
    }
    int res = (f+1) * cnt[1];
    for( int i = 2 ; i <= 7 ; i ++ )
        res += i * cnt[i];
    cout << res;
    return 0;
}

D-Deranging Hat

因为不要求操作次数最少,我以计算出目标串,然后每次暴力的交换即可。

#include <bits/stdc++.h>

using namespace std;

#define int long long

int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    string s , t;
    cin >> s;
    t = s;
    int n = s.size();
    sort(s.begin(), s.end());
    for( int i = 0 ; i < n ; i ++ ){
        if( s[i] == t[i] ) continue;
        for( int j = i + 1 ; j < n ; j ++ ){
            if( s[j] != t[i] ) continue;
            if( s[i] < s[j] )  cout << j+1 << " " << i+1 << "\n";
            else cout << i+1 << " " << j+1 << "\n";
            swap( s[i] , s[j] );
            break;
        }
    }
    return 0;
}

E-Education

贪心做就好了,按照价格从下到大遍历防止,每次选择满足最多人数的部门即可。

#include <bits/stdc++.h>

using namespace std;

#define int long long

#define mp make_pair
typedef pair<int, int> pii;
typedef array<int, 3> T;


int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    set<pii> s;
    for (int i = 1, x; i <= n; i++)
        cin >> x, s.emplace(x, i);
    vector<T> h(m);
    for (int i = 0; i < m; i++)
        cin >> h[i][0];
    for (int i = 0; i < m; i++)
        cin >> h[i][1], h[i][2] = i + 1;
    sort(h.begin(), h.end(), [](T x, T y) {
        if (x[1] != y[1]) return x[1] < y[1];
        return x[0] > y[0];
    });
    vector<int> res(n + 1);
    for (auto it: h) {
        if (s.empty()) break;
        auto i = s.lower_bound(mp(it[0], 0));
        if (i == s.end()) {
            i--;
            res[i->second] = it[2];
            s.erase(i);
        } else {
            while (i != s.begin() and i->first > it[0]) i--;
            if (i->first <= it[0])
                res[i->second] = it[2], s.erase(i);
        }
    }
    if (s.empty()) {
        for (int i = 1; i <= n; i++)
            cout << res[i] << " ";
        cout << "\n";
        return 0;
    }
    cout << "impossible\n";
    return 0;
}

F-Flipping Coins

概率 dp,状态为\(f[i][j]\)表示前\(i\)次操作,\(j\)枚硬币朝上的概率。

当$j\ne n $时,我们选择一枚原本背面朝上的硬币所以

\[f[i+1][j] += f[i][j] \times 0.5\\ f[i+1][j+1] += f[i][j] \times 0.5 \]

\(j=n\)时,我们只能选择原本正面朝上的硬币所以

\[f[i+1][j] += f[i][j]\times 0.5\\ f[i+1][j-1] += f[i][j] \times 0.5 \]

答案就是\(\sum f[k][j]\times j\)

#include <bits/stdc++.h>

using namespace std;

#define int long long

typedef long double ldb;

int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n , k;
    cin >> n >> k;
    vector f( k+1 , vector<ldb>( n+1 ) );
    f[0][0] = 1;
    for( int i = 0 ; i < k ; i ++ ){
        for( int j = 0 ; j < n ; j ++ )
            f[i+1][j] += f[i][j] * 0.5 , f[i+1][j+1] += f[i][j] * 0.5;
        f[i+1][n] += f[i][n] * 0.5 , f[i+1][n-1] += f[i][n] * 0.5;
    }
    ldb res = 0;
    for( int i = 0 ; i <= n ; i ++ )
        res += f[k][i] * i;
    cout << fixed << setprecision(10) << res << "\n";
    return 0;
}

G-GentleBots

根据题意模拟就好了,解决冲突的方案有很多种。这里介绍一种,当两个机器人在同一点时,撤销最后一次移动并项任意其他方向移动一步。

#include <bits/stdc++.h>

using namespace std;

struct node {
    int x, y, z;

    node(int x = 0, int y = 0, int z = 0) : x(x), y(y), z(z) {};

    bool operator==(const node &b) const {
        return x == b.x and y == b.y and z == b.z;
    }
};

istream &operator>>(istream &is, node &_) {
    return is >> _.x >> _.y >> _.z;
}

ostream &operator<<(ostream &os, node &_) {
    return os << "(" << _.x << " " << _.y << " " << _.z << ")";
}

int32_t main() {
    ios::sync_with_stdio(0), cin.tie(0);
    node a, ea, b, eb, da, db;
    cin >> a >> ea >> b >> eb;
    cout << a << " " << b << "\n";
    while (true) {
        if ((a == ea) and (b == eb)) return 0;
        da = a, db = b;
        if (da.x != ea.x) {
            if (da.x < ea.x) da.x++; else da.x--;
            if (da == b) da = a, da.y--;
        } else if (a.y != ea.y) {
            if (da.y < ea.y) da.y++; else da.y--;
            if (da == b) da = a, da.z--;
        } else if (a.z != ea.z) {
            if (da.z < ea.z) da.z++; else da.z--;
            if (da == b) da = a, da.x--;
        }
        if (db.x != eb.x) {
            if (db.x < eb.x) db.x++; else db.x--;
            if (db == da) db = b, db.y--;
        } else if (db.y != eb.y) {
            if (db.y < eb.y) db.y++; else db.y--;
            if (db == da) db = b, db.z--;
        } else if (db.z != eb.z) {
            if (db.z < eb.z) db.z++; else db.z--;
            if (db == da) db = b, db.x--;
        }
        cout << da << " " << db << "\n";
        a = da, b = db;
    }
    return 0;
}

H-Hiker Safety

限制条件是相邻两个个人\(i,i+1\)之间的距离必须在\([\max(A_i,A_{i+1}) , B]\)之间,每次只能有一个人移动到下一个点。问是否存在方案使得所有人可以到达终点,有输出方案。

用队列的储存当前可以移动的人数,每次移动后再次判断是否可以移动,可以重新放入队列中。同时还要判断相邻的人是否可以移动,可以也要放进队列中。

其实还有另一种思路是保证队列中每个人只出现一次,这样就要判断相邻的人是否是本来不可以移动但是现在可以移动。

两种思路类似,这里我这里实现了第一种思路。

#include <bits/stdc++.h>

using namespace std;

int32_t main() {
    ios::sync_with_stdio(0), cin.tie(0);
    int B, m;
    cin >> B >> m;
    vector<int> d(m + 1);
    for (int i = 1; i <= m; i++) cin >> d[i];
    int n;
    cin >> n;
    vector<int> a(n + 1), pos(n + 1), res;
    for (int i = 1; i <= n; i++) cin >> a[i] >> pos[i];
    queue<int> q;
    auto check = [&](int x) {
        if (pos[x] == m) return false;
        int lst = x - 1, nxt = x + 1;
        if (nxt > n or pos[nxt] >= m) nxt = 0;
        if (lst) {
            if (abs(d[pos[x] + 1] - d[pos[lst]]) > B) return false;
            if (abs(d[pos[x] + 1] - d[pos[lst]]) < max(a[lst], a[x])) return false;
        }
        if (nxt) {
            if (abs(d[pos[x] + 1] - d[pos[nxt]]) > B)return false;
            if (abs(d[pos[x] + 1] - d[pos[nxt]]) < max(a[nxt], a[x])) return false;
        }
        return true;
    };
    auto op = [&](int x) {
        pos[x]++, res.push_back(x);
        if (x > 1) q.push(x - 1);
        if (x <= n && pos[x] <= m) q.push(x);
        if (x + 1 <= n && pos[x + 1] <= m) q.push(x + 1);
    };
    for (int i = 1; i <= n; i++)
        if (pos[i] < m) q.push(i);
    while (!q.empty()) {
        int x = q.front();
        q.pop();
        if (check(x)) op(x);
    }
    for (int i = 1; i <= n; i++)
        if (pos[i] < m) printf("impossible\n"), exit(0);
    for (auto i: res) cout << i << " ";
    return 0;
}

I-I Work All Day

#include <bits/stdc++.h>

using namespace std;

#define int long long

int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n , t;
    cin >> n;
    vector<int> a(n);
    for( auto &i : a ) cin >> i;
    cin >> t;
    int res , val = INT_MAX;
    for( auto i : a )
        if( val > t % i ) val = t % i , res = i;
    cout << res << "\n";
    return 0;
}

J-Just A Minim

签到题

#include <bits/stdc++.h>

using namespace std;

int32_t main() {
    ios::sync_with_stdio(0), cin.tie(0);
    double res = 0;
    int n;
    cin >> n;
    for( double x ; n ; n -- ){
        cin >> x;
        if( x == 0 ) res += 2;
        else res += 1.0 / x;
    }
    cout << fixed << setprecision(10) << res << "\n";
    return 0;
}

K-Knightsbridge

网络流

L-Lounge Lizards

这道题实际上我们可以把蜥蜴按照向量去划分一下,在同一组中按照距离排序,然后再同一组内希望递增的子序列尽可能的长,就是最长上升子序列了。这道题关于按照向量划分其实还是很板的。

#include <bits/stdc++.h>

using namespace std;

#define int long long

#define x first
#define y second
#define mp make_pair
using pii = pair<int, int>;
using Point = pii;
using Vec = pii;

Point operator-(Point A, Point B) {
    return mp(B.x - A.x, B.y - A.y);
}

int32_t main() {
    ios::sync_with_stdio(0), cin.tie(0);
    Point s, v, vn;
    cin >> s.x >> s.y;
    int n;
    cin >> n;
    map<Vec, vector<pii> > cnt;
    for (int i = 1, h, d; i <= n; i++) {
        cin >> v.x >> v.y >> h;
        v = v - s, d = abs(gcd(v.x, v.y));
        vn = Vec(v.x / d, v.y / d);
        cnt[vn].emplace_back(abs(v.x) + abs(v.y), h);
    }
    int res = 0;
    for (auto [v, p]: cnt) {
        sort(p.begin(), p.end());
        vector<int> lis;
        for (auto [d, h]: p) {
            if (lis.empty() or lis.back() < h) lis.push_back(h);
            else *lower_bound(lis.begin(), lis.end(), h) = h;
        }
        res += lis.size();
    }
    cout << res << "\n";
    return 0;
}
posted @ 2023-08-10 14:33  PHarr  阅读(94)  评论(0编辑  收藏  举报