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 $时,我们选择一枚原本背面朝上的硬币所以
当\(j=n\)时,我们只能选择原本正面朝上的硬币所以
答案就是\(\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;
}