2020杭电多校第四场题解
2020 Multi-University Training Contest 4
施工中。。。
1002 Blow up the Enemy
签到题,只需要计算每把武器造成至少100伤害的时间,求出最少时间武器的数量,然后计算胜率即可。
比赛时一发过。
#include<bits/stdc++.h>
#define ll long long
#define maxn 100010
#define mod 1000000007
using namespace std;
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n;
scanf("%d", &n);
int sum = 0, ma = 1e9;
for (int i = 0; i < n; i++) {
int x, y;
scanf("%d%d", &x, &y);
int tp = ((100 + x - 1) / x - 1) * y;
if (tp < ma) ma = tp, sum = 1;
else if (tp == ma) sum++;
}
printf("%.8lf\n", 1.0 - 0.5 * sum / n);
}
return 0;
}
1003 Contest of Rope Pulling
题目看上去像是一个背包问题,但如果使用朴素的背包方法会T。
所以可以将物品的容量分为正负两种,依次进行背包操作,答案保持在0处。
由于物品大小不大,所以对数组进行随机排序,缩小背包的大小,答案正确率处于很高水平。
最后一小时尝试过,使用按大小排序方式不行,然后随机排序后就过了,只能说是运气好。
#include<bits/stdc++.h>
#define ll long long
#define maxn 300010
#define mod 1000000007
using namespace std;
ll dp[maxn];
ll inf = -1e15;
struct cv {
ll x, y;
}a[maxn], b[maxn], c[maxn];
bool cmp(cv p, cv q) {
return p.y > q.y;
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++) scanf("%lld%lld", &a[i].x, &a[i].y);
for (int i = 0; i < m; i++) scanf("%lld%lld", &b[i].x, &b[i].y);
random_shuffle(a, a + n);
random_shuffle(b, b + m);
int cnt = 0;
for (int i = 0; i < min(n, m); i++) {
c[cnt++] = a[i];
b[i].x *= -1;
c[cnt++] = b[i];
}
if (n > m) for (int i = m; i < n; i++) c[cnt++] = a[i];
else for (int i = n; i < m; i++) {
b[i].x *= -1; c[cnt++] = b[i];
}
for (int i = 0; i < maxn; i++) dp[i] = inf;
dp[maxn / 2] = 0;
for (int i = 0; i < cnt; i++) {
if (c[i].x > 0) {
for (ll j = maxn - 1; j - c[i].x >= 0; j--) {
dp[j] = max(dp[j], dp[j - c[i].x] + c[i].y);
}
}
else {
for (ll j = 0; j - c[i].x < maxn; j++) dp[j] = max(dp[j], dp[j - c[i].x] + c[i].y);
}
}
printf("%lld\n", dp[maxn / 2]);
}
return 0;
}
1004 Deliver the Cake
求最短路的模板题,在计算距离的时候处理一下是左手还是右手拿蛋糕就可以了。
由于中途转换过程处理出了一些问题导致WA2,想清楚后就解决了。
#include<bits/stdc++.h>
#define ll long long
#define maxn 200010
#define mod 1000000007
using namespace std;
vector<int>b[maxn];
vector<ll>c[maxn];
char a[maxn];
int pe[maxn];
ll ans[maxn][3];
struct cv {
int x, pe;
ll y;
friend bool operator<(cv p, cv q) {
return p.y > q.y;
}
}dd, bb;
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n, m, s, e;
ll x;
scanf("%d%d%d%d%lld", &n, &m, &s, &e, &x);
scanf("%s", a + 1);
for (int i = 1; i <= n; i++) {
if (a[i] == 'L') pe[i] = 1;
else if (a[i] == 'R') pe[i] = 2;
else pe[i] = 3;
vector<int>v;
swap(v, b[i]);
vector<ll>vv;
swap(vv, c[i]);
ans[i][1] = ans[i][2] = 0;
}
for (int i = 0; i < m; i++) {
int l, r;
ll z;
scanf("%d%d%lld", &l, &r, &z);
b[l].push_back(r);
c[l].push_back(z);
b[r].push_back(l);
c[r].push_back(z);
}
dd.x = s, dd.y = 0;
priority_queue<cv>q;
if (pe[s] & 1) {
dd.pe = 1;
q.push(dd);
}
if (pe[s] & 2) {
dd.pe = 2;
q.push(dd);
}
while (!q.empty()) {
dd = q.top();
q.pop();
int now = dd.x;
if (dd.y != ans[now][dd.pe]) continue;
if (now == e) break;
for (int i = 0; i < b[now].size(); i++) {
bb.x = b[now][i], bb.y = dd.y + c[now][i];
if ((dd.pe & pe[bb.x]) == 0) {
bb.pe = pe[bb.x];
bb.y += x;
}
else bb.pe = dd.pe;
if (ans[b[now][i]][bb.pe] == 0) {
ans[b[now][i]][bb.pe] = bb.y;
q.push(bb);
}
else if (ans[b[now][i]][bb.pe] > bb.y) {
ans[b[now][i]][bb.pe] = bb.y;
q.push(bb);
}
}
}
if (ans[e][1] == 0) printf("%lld\n", ans[e][2]);
else if (ans[e][2] == 0) printf("%lld\n", ans[e][1]);
else printf("%lld\n", min(ans[e][1], ans[e][2]));
}
return 0;
}
1005 Equal Sentences
对于每个相邻字符不同的串,相邻的两个数可以交换,但是不能连续交换
通过 \(dp\) 可以得到,不同的串的个数就是斐波那契数列
计算的时候,连续相同的数阶段截断即可
比赛时一发过。
#include<bits/stdc++.h>
#define ll long long
#define maxn 200010
#define mod 1000000007
using namespace std;
int a[maxn];
ll dp[maxn];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
int t;
cin >> t;
dp[0] = 1;
dp[1] = 1;
for (int i = 2; i < maxn; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
dp[i] %= mod;
}
while (t--) {
int n;
map<string, int>mp;
int cnt = 0;
cin >> n;
for (int i = 1; i <= n; i++) {
string tp;
cin >> tp;
if (mp.count(tp)) a[i] = mp[tp];
else {
mp[tp] = cnt;
a[i] = cnt++;
}
}
ll ans = 1;
ll sum = 1;
for (int i = 2; i <= n; i++) {
if (a[i] == a[i - 1]) {
ans = ans * dp[sum] % mod;
sum = 1;
}
else sum++;
}
ans = ans * dp[sum] % mod;
printf("%lld\n", ans);
}
return 0;
}
1007 Go Running
对于每条记录,我们令他都是有一个从 \(t=0\) 的时刻就开始跑的人经过的,那这个人可以同时经过的点都是最多的
对于每条记录,可能会有两个人,即一个从东向西跑,一个从西向东跑
我们对两个人建边,就可以建成一个二分图
对于每条边都需要一条个点,即最小点覆盖
原先用贪心思想WA了一发,使用不太靠谱的模板T了一发,模板改错又WA了一发,最后发现问题才过了。
#include<bits/stdc++.h>
#define ll long long
#define mod 1000000007
using namespace std;
const int MAXN = 200010;
const int MAXM = 200010;
const int INF = 2147483647;
struct Edge {
int v;
int next;
} edge[MAXM];
int nx, ny;
int cnt;
int t;
int dis;
int first[MAXN];
int xlink[MAXN], ylink[MAXN];
/*xlink[i]表示左集合顶点所匹配的右集合顶点序号,ylink[i]表示右集合i顶点匹配到的左集合顶点序号。*/
int dx[MAXN], dy[MAXN];
/*dx[i]表示左集合i顶点的距离编号,dy[i]表示右集合i顶点的距离编号*/
int vis[MAXN]; //寻找增广路的标记数组
void init() {
cnt = 0;
memset(first, -1, sizeof(first));
memset(xlink, -1, sizeof(xlink));
memset(ylink, -1, sizeof(ylink));
}
void read_graph(int u, int v) {
edge[cnt].v = v;
edge[cnt].next = first[u], first[u] = cnt++;
}
int bfs() {
queue<int> q;
dis = INF;
memset(dx, -1, sizeof(dx));
memset(dy, -1, sizeof(dy));
for (int i = 0; i < nx; i++) {
if (xlink[i] == -1) {
q.push(i);
dx[i] = 0;
}
}
while (!q.empty()) {
int u = q.front();
q.pop();
if (dx[u] > dis) break;
for (int e = first[u]; e != -1; e = edge[e].next) {
int v = edge[e].v;
if (dy[v] == -1) {
dy[v] = dx[u] + 1;
if (ylink[v] == -1) dis = dy[v];
else {
dx[ylink[v]] = dy[v] + 1;
q.push(ylink[v]);
}
}
}
}
return dis != INF;
}
int find(int u) {
for (int e = first[u]; e != -1; e = edge[e].next) {
int v = edge[e].v;
if (!vis[v] && dy[v] == dx[u] + 1) {
vis[v] = 1;
if (ylink[v] != -1 && dy[v] == dis) continue;
if (ylink[v] == -1 || find(ylink[v])) {
xlink[u] = v, ylink[v] = u;
return 1;
}
}
}
return 0;
}
int MaxMatch() {
int ans = 0;
while (bfs()) {
memset(vis, 0, sizeof(vis));
for (int i = 0; i < nx; i++) if (xlink[i] == -1) {
ans += find(i);
}
}
return ans;
}
int main() {
int tt;
scanf("%d", &tt);
while (tt--) {
init();
int n;
scanf("%d", &n);
map<ll, int>mp1, mp2;
int cntt = 0;
set<ll>mep;
for (int i = 0; i < n; i++) {
ll x, y;
scanf("%lld%lld", &x, &y);
ll tu = x * (1e9 + 1) + y;
if (mep.count(tu)) continue;
else mep.insert(tu);
ll id1, id2;
if (mp1.count(x - y)) id1 = mp1[x - y];
else {
mp1[x - y] = cntt;
id1 = cntt;
cntt++;
}
if (mp2.count(x + y)) id2 = mp2[x + y];
else {
mp2[x + y] = cntt;
id2 = cntt;
cntt++;
}
read_graph(id1, id2);
}
nx = cntt; ny = cntt;
int ans = MaxMatch();
printf("%d\n", ans);
}
return 0;
}
1011 Kindergarten Physics
由于万有引力太小,可以忽略不记,直接输出原距离即可。
比赛时一发过。
#include <bits/stdc++.h>
#define db double
using namespace std;
const db g = 6.67430 * 1e-11;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
int t; cin >> t;
while (t--) {
db a, b, d, t;
cin >> a >> b >> d >> t;
cout << d << '\n';
}
return 0;
}
1012 Last Problem
构造题,对于每个数字写下之前保证周围有比它小1-4的数字,所以考虑递归先放小的数字。
同时记录小每个位置的数字,如果已经放上了要求的数,便无需再填写。
比赛时一发过。
#include<bits/stdc++.h>
#define ll long long
#define maxn 300010
#define mod 1000000007
using namespace std;
int a[2000][2000];
int to[4][2] = { 0,-1,1,0,-1,0,0,1 };
void sol(int n, int x, int y) {
for (int i = 0; i < 4; i++) {
if (a[x + to[i][0]][y + to[i][1]] == n - i - 1) continue;
if (n - i - 1 >= 1) sol(n - i - 1, x + to[i][0], y + to[i][1]);
}
printf("%d %d %d\n", x, y, n);
a[x][y] = n;
}
int main() {
int n;
scanf("%d", &n);
sol(n, 1000, 1000);
return 0;
}