ACM练习坑(待填)
这几天刚到学校,空闲时间比较多,又加入了ACM队,打算重整信心捡起以前的知识,现在就来阅题填坑啦
以下题目来自codeforces
我已经做了:
15/50
Codeforces Round #433 (Div. 1, based on Olympiad of Metropolises)
A.Planning
有n(<=300000)个飞机,第i个飞机原计划在i时刻起飞。现在机场延误,需要让这n个飞机在[ k , k+n ]时刻全部飞完,每个时刻只能飞一个飞机,同时每个飞机不能比原计划更早起飞,也就是飞机i的起飞区间应该是[ i , n+k ]。每个飞机有一个延误系数,这个飞机的延误代价=延误系数*延误时间(如果按照原计划时间起飞则延误代价=0)。求最小延误代价,以及每个飞机的起飞时刻。
贪心很容易想到,但是有时间的限制,算作二维问题
就让当前状态满足一维的限制,另一维取最优即可
即当前加入的都是可选状态。
#include <bits/stdc++.h> #define maxn 300010 using namespace std; struct Node { int c, id; } p[maxn]; bool operator < (const Node& a, const Node& b) { return a.c < b.c; } priority_queue<Node> q; int n, k; int main() { scanf("%d%d", &n, &k); for(int i = 1; i <= n; ++ i) scanf("%d", &p[i].c), p[i].id = i; for(int i = 1; i <= k; ++ i) q.push(p[i]); long long ans = 0; for(int i = k+1; i <= k+n; ++ i) { if(i <= n) q.push(p[i]); Node now = q.top(); q.pop(); p[now.id].id = i; ans += (long long)(i-now.id)*now.c; } printf("%I64d\n", ans); for(int i = 1; i <= n; ++ i) printf("%d ", p[i].id); return 0; }
B.Jury Meeting
现在有(n+1)个城市,其中第1~n个城市每个城市有一个人,第0个城市是人们需要聚集的地方。还有m条航班,每条航班从0到其他城市或从其他城市到0,当天即可抵达,现在需要选定一个时间段,长度为k天,使得这一个时间段里人们都在0城市工作(航班抵达和离开0城市的那一天不能进行工作),问把n个人送到0城市,工作完成后再送回去的最小花费。
贪心+预处理
#include <bits/stdc++.h> #define maxn 1000010 using namespace std; int n, m, k; struct Node { int d, f, t, c; } p[maxn]; bool operator < (const Node& a, const Node& b) { return a.d < b.d; } long long ans1[maxn]; long long ans2[maxn]; int vis[maxn]; int cnt; int main() { scanf("%d%d%d", &n, &m, &k); for(int i = 1; i <= m; ++ i) scanf("%d%d%d%d", &p[i].d, &p[i].f, &p[i].t, &p[i].c); sort(p+1, p+1+m); memset(ans1, -1, sizeof ans1); memset(ans2, -1, sizeof ans2); memset(vis, 0, sizeof vis); cnt = 0; long long sum = 0; for(int i = 1; i <= m; ++ i) { if(p[i].f) { if(vis[p[i].f]) { if(p[i].c < vis[p[i].f]) sum = sum-vis[p[i].f]+p[i].c, vis[p[i].f] = p[i].c; } else { vis[p[i].f] = p[i].c; sum += p[i].c; cnt ++; } } if(cnt == n) ans1[p[i].d] = sum; } memset(vis, 0, sizeof vis); cnt = 0; sum = 0; for(int i = m; i; -- i) { if(p[i].t) { if(vis[p[i].t]) { if(p[i].c < vis[p[i].t]) sum = sum-vis[p[i].t]+p[i].c, vis[p[i].t] = p[i].c; } else { vis[p[i].t] = p[i].c; sum += p[i].c; cnt ++; } } if(cnt == n) ans2[p[i].d] = sum; } int mx = 0; for(int i = 1; i <= m; ++ i) mx = max(mx, p[i].d); for(int i = 1; i <= mx; ++ i) if(ans1[i-1] != -1) { if(ans1[i] == -1) ans1[i] = ans1[i-1]; else ans1[i] = min(ans1[i], ans1[i-1]); } for(int i = mx; i; -- i) if(ans2[i+1] != -1) { if(ans2[i] == -1) ans2[i] = ans2[i+1]; else ans2[i] = min(ans2[i], ans2[i+1]); } long long ret = -1; for(int i = 1; i <= mx-k; ++ i) { if(ans1[i] > 0 && ans2[i+k+1] > 0) { if(ret == -1) ret = ans1[i]+ans2[i+k+1]; else ret = min(ret, ans1[i]+ans2[i+k+1]); } } printf("%I64d\n", ret); return 0; }
C.Boredom
主席树,二维前缀和
这样就可以快速的查询某个子矩阵里标记点的个数,对于每个查询的矩阵,我们只需要求不在这个矩阵里的所有 beautiful rectangles个数即可,也就是两个被标记的点都在查询矩阵的上方,下方,左方,右方,最后在去一下重。
#include <bits/stdc++.h> #define maxn 200005 using namespace std; typedef long long ll; int n, q; int lc[maxn*30], rc[maxn*30], size[maxn*30]; int cnt = 0, t[maxn], p[maxn]; int update(int root, int l, int r, int x, int k) { int now = ++ cnt; size[now] = size[root] + k; if(l == r) return now; int mid = l + r >> 1; if(x <= mid) { lc[now] = update(lc[root], l, mid, x, k); rc[now] = rc[root]; } else { lc[now] = lc[root]; rc[now] = update(rc[root], mid+1, r, x, k); } return now; } int query(int root, int l, int r, int L, int R) { if(root == 0 || L > R || L < 1 || R > n) return 0; if(L == l && R == r) return size[root]; int mid = l + r >> 1; if(R <= mid) return query(lc[root], l, mid, L, R); if(L > mid) return query(rc[root], mid+1, r, L, R); return query(lc[root], l, mid, L, mid)+query(rc[root], mid+1, r, mid+1, R); } ll f(int x) { return (ll)x*(x-1)/2; } int main() { scanf("%d%d", &n, &q); for(int i = 1; i <= n; ++ i) { scanf("%d", &p[i]); t[i] = update(t[i-1], 1, n, p[i], 1); } ll ans = 0; int l, d, r, u; while(q --) { scanf("%d%d%d%d", &l, &d, &r, &u); int t1 = query(t[l-1], 1, n, 1, n); int t2 = query(t[n], 1, n, 1, d-1); int t3 = query(t[n], 1, n, 1, n) - query(t[r], 1, n, 1, n); int t4 = query(t[n], 1, n, u+1, n); int t5 = query(t[l-1], 1, n, 1, d-1); int t6 = t2-query(t[r], 1, n, 1, d-1); int t7 = query(t[l-1], 1, n, u+1, n); int t8 = query(t[n], 1, n, u+1, n)-query(t[r], 1, n, u+1, n); ans = f(n)-(f(t1)+f(t2)+f(t3)+f(t4)-f(t5)-f(t6)-f(t7)-f(t8)); printf("%I64d\n", ans); } return 0; }
Codeforces Round #432 (Div. 1, based on IndiaHacks Final Round 2017)
A.需要一些数学脑洞??范围1000??
It's easier to visualize this in 2D first. Fix a point p. If all other points form angles that are 90 degrees or greater, they must all be in different quadrants, so there can be at most 4 such points. In k dimension, this generalizes to 2*k such points, so for five dimensions, there can only be at most 10 other points. Thus, we can run the naive solution for small n and print 0 otherwise.
#include <bits/stdc++.h> #define maxn 105 using namespace std; struct Point { int x[5]; void read() { for(int i = 0; i < 5; ++ i) scanf("%d", &x[i]); } }p[maxn]; long long operator * (const Point& a, const Point& b) { long long ret = 0; for(int i = 0; i < 5; ++ i) ret += (long long)a.x[i]*b.x[i]; return ret; } Point operator - (const Point& a, const Point& b) { Point ret; for(int i = 0; i < 5; ++ i) ret.x[i] = a.x[i] - b.x[i]; return ret; } int n; bool fg; vector<int> G; bool pd(int x, int y, int z) { return (p[y]-p[x]) * (p[z]-p[x]) > 0; } int main() { scanf("%d", &n); if(n > 100) { printf("0\n"); return 0; } for(int i = 1; i <= n; ++ i) p[i].read(); for(int i = 1; i <= n; ++ i) { fg = true; for(int j = 1; j <= n; ++ j) { for(int k = j+1; k <= n; ++ k) if(i != j && i != k) { if(pd(i, j, k)) { fg = false; break; } } if(!fg) break; } if(fg) G.push_back(i); } printf("%d\n", G.size()); for(int i = 0; i < G.size(); ++ i) printf("%d\n", G[i]); return 0; }
B.Arpa and a list of numbers
枚举因子然后就好做了
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2000010; const ll inf = 1e18; ll s[maxn]; int a[maxn]; bool used[maxn]; int n; ll x, y; int main(){ scanf("%d%I64d%I64d", &n, &x, &y); int p = x / y; for(int i = 1; i <= n; ++ i) { int t; scanf("%d", &t); a[t] ++; s[t] += t; } ll ans = inf; for(int i = 1; i < maxn; ++ i) a[i] += a[i-1], s[i] += s[i-1]; for(int i = 2; i <= 1000000; ++ i) { if(used[i]) continue; ll tmp = 0; for(int j = i; j <= 1000000+i; j += i) { used[j] = true; int k = max(j-i+1, j-p); tmp += ((ll)(a[j]-a[k-1])*j - (s[j]-s[k-1]))*y + (ll)(a[k-1]-a[j-i])*x; } ans = min(ans, tmp); } printf("%I64d", ans); return 0; }
我能说我的A题没看懂嘛。。
B.Minimum and Maximum
This is an interactive problem. You have to use flush operation right after printing each line. For example, in C++ you should use function fflush(stdout)
交互式询问一个数列的最大最小值
两个分成一组查询
#include<bits/stdc++.h> using namespace std; char ask(int x, int y) { printf("? %d %d\n", x, y); fflush(stdout); char c; cin >> c; return c; } void solve() { int n; cin >> n; if(n == 1) { printf("! 1 1\n"); return; } char c = ask(1, 2); int mx, mn; if(c == '=') mx = mn = 1; if(c == '<') mx = 2, mn = 1; if(c == '>') mx = 1, mn = 2; for(int i = 3; i <= n; i += 2) { if(i == n) { if(ask(mx, i) == '<') mx = i; if(ask(mn, i) == '>') mn = i; } else { c = ask(i, i+1); if(c == '>') { if(ask(mx, i) == '<') mx = i; if(ask(mn, i+1) == '>') mn = i+1; } else { if(ask(mn, i) == '>') mn = i; if(ask(mx, i+1) == '<') mx = i+1; } } } printf("! %d %d\n", mn, mx); fflush(stdout); } int main() { int Test; scanf("%d",&Test); while (Test--) solve(); return 0; }
C.Bulmart
竟然养成了过了样例就交的坏习惯
小错误不断啊。。
错因是没有开longlong,两数相乘的范围没有看到,最后还写T了。。感觉很绝望
然后把bfs的常数省了省,好像还是不行。
唔。把二分里套的sort省掉以后就A了。。
#include <bits/stdc++.h> #define maxn 5010 using namespace std; int n, m; struct Edge { int to, nxt; }edge[maxn*maxn*2]; int h[maxn], cnt; void addedge(int u, int v) { ++ cnt; edge[cnt].to = v; edge[cnt].nxt = h[u]; h[u] = cnt; } struct qwq { int num, price, dis; } test; bool operator < (const qwq& a, const qwq& b) { return a.price < b.price; } vector<pair<int, int> > Gpa[maxn]; vector<qwq> ans; int G, R, A; int tim, vis[maxn], que[maxn], dis[maxn]; void bfs() { ans.clear(); int head = 0, tail = 0; que[tail ++] = G; vis[G] = ++ tim; dis[G] = 0; while(head != tail) { int u = que[head ++]; for(int i = 0; i < Gpa[u].size(); ++ i) { test.dis = dis[u]; test.num = Gpa[u][i].second; test.price = Gpa[u][i].first; ans.push_back(test); } for(int i = h[u]; i; i = edge[i].nxt) { int v = edge[i].to; if(vis[v] != tim) { vis[v] = tim; dis[v] = dis[u]+1; que[tail ++] = v; } } } sort(ans.begin(), ans.end()); } bool judge(int x) { long long nw = 0; int r = R; for(int i = 0; i < ans.size(); ++ i) { qwq& test = ans[i]; if(test.dis > x) continue; if(r > test.num) { r -= test.num; nw += (long long)test.num*test.price; if(nw > A) return false; } else { nw += (long long)r*test.price; if(nw > A) return false; else return true; } } return false; } void solve() { bfs(); int l = 0, r = n; while(l < r) { int mid = l + (r-l)/2; if(judge(mid)) r = mid; else l = mid+1; } if(l != n)printf("%d\n", l); else printf("-1\n"); } int main() { scanf("%d%d", &n, &m); int u, v, d, q; for(int i = 1; i <= m; ++ i) { scanf("%d%d", &u, &v); addedge(u, v); addedge(v, u); } scanf("%d", &m); for(int i = 1; i <= m; ++ i) { scanf("%d%d%d", &u, &v, &d); Gpa[u].push_back(make_pair(d, v)); } scanf("%d", &q); while(q --) { scanf("%d%d%d", &G, &R, &A); solve(); } return 0; }
D.Running Over the bridges
感觉像守望者的逃离。。贪心尽量放在后面
#include <bits/stdc++.h> #define maxn 200010 using namespace std; vector<long long> ans; int n; long long r, l[maxn], t[maxn]; int main() { scanf("%d%I64d", &n, &r); for(int i = 1; i <= n; ++ i) scanf("%I64d", &l[i]); for(int i = 1; i <= n; ++ i) scanf("%I64d", &t[i]); long long re = 0, nw = 0, tot = 0; for(int i = 1; i <= n; ++ i) { if(t[i] < l[i]) { printf("-1\n"); return 0; } if(re >= l[i]) { re -= l[i]; nw += l[i]; continue; } l[i] -= re; t[i] -= re; nw += re; re = 0; if(l[i]*2 <= t[i]) nw += l[i]*2; else { long long T = t[i] - l[i]; l[i] -= T; t[i] -= T*2; nw += T*2; for(long long j = nw; j < nw+t[i] && ans.size() < 100010; j += r) ans.push_back(j); nw += l[i]; tot += l[i] / r; re = l[i] % r; if(re) { tot ++; re = r - re; } } } if(ans.size() == 0) printf("0\n"); else { printf("%I64d\n", tot); if(ans.size() <= 100000) { sort(ans.begin(), ans.end()); for(int i = 0; i < ans.size(); ++ i) printf("%I64d ", ans[i]); } } return 0; }
E.关于排行榜的滚动,先按照成绩排一个名,d值为正的从得分score小到大搞一下,d值为负的score从大倒小搞一下
#include <bits/stdc++.h> #define maxn 110 using namespace std; int n; bool vis[maxn]; struct People { int val, d, id; }p[maxn]; bool operator < (const People& a, const People& b) { if(a.val != b.val) return a.val > b.val; return a.id < b.id; } //优先级顺序 int main() { scanf("%d", &n); for(int i = 1; i <= n; ++ i) { scanf("%d%d", &p[i].val, &p[i].d); p[i].id = i; } sort(p+1, p+1+n); int ans = 0; for(int i = n; i >= 1; -- i) { if(vis[p[i].id]) continue; if(p[i].d < 0) continue; vis[p[i].id] = 1; p[i].val += p[i].d; for(int j = i-1; j; -- j) { if(p[j+1] < p[j]) { ans ++; swap(p[j], p[j+1]); } else break; } ++ i; } for(int i = 1; i <= n; ++ i) { if(vis[p[i].id]) continue; vis[p[i].id] = 1; p[i].val += p[i].d; for(int j = i+1; j <= n; ++ j) { if(p[j] < p[j-1]) { ans ++; swap(p[j-1], p[j]); } else break; } -- i; } printf("%d\n", ans); return 0; }
G.Car Repair Shop
模拟区间查询
#include <bits/stdc++.h> #define maxn 210 using namespace std; int n, s[maxn], d[maxn]; int judge(int idx, int l, int r) { for(int i = 1; i < idx; ++ i) if(max(s[i], l) <= min(s[i]+d[i]-1, r)) return i; return 0; } int main() { scanf("%d", &n); d[0] = 1; for(int i = 1; i <= n; ++ i) { scanf("%d%d", &s[i], &d[i]); int idx = judge(i, s[i], s[i]+d[i]-1); if(!idx) continue; s[i] = 1; idx = judge(i, s[i], s[i]+d[i]-1); while(idx) { s[i] = s[idx] + d[idx]; idx = judge(i, s[i], s[i]+d[i]-1); } } for(int i = 1; i <= n; ++ i) printf("%d %d\n", s[i], s[i]+d[i]-1); return 0; }
H.Delete Them
读懂题意是关键
#include <bits/stdc++.h> using namespace std; char s[110][110]; int n, m, x; vector<int> G; bool vis[110]; char ans[110]; int main() { scanf("%d%d", &n, &m); for(int i = 1; i <= n; ++ i) scanf("%s", s[i]+1); int len = -1; for(int i = 1; i <= m; ++ i) { scanf("%d", &x); G.push_back(x); vis[x] = true; if(len == -1) len = strlen(s[x]+1); else if(strlen(s[x]+1) != len) { printf("No\n"); return 0; } } for(int i = 1; i <= len; ++ i) { char c = s[G[0]][i]; int fg = 0; for(int j = 1; j < G.size(); ++ j) if(s[G[j]][i] != c) { ans[i] = '?'; fg = -1; break; } if(!fg) ans[i] = c; } for(int i = 1; i <= n; ++ i) { if(!vis[i] && strlen(s[i]+1) == len) { int fg = 0; for(int j = 1; j <= len; ++ j) { if(ans[j] == '?') continue; if(ans[j] != s[i][j]) { fg = -1; break; } } if(!fg) { printf("No"); return 0; } } } printf("Yes\n"); for(int i = 1; i <= len; ++ i) putchar(ans[i]); return 0; }
I.Olympiad in Programming and Sports
费用流
#include <bits/stdc++.h> #define maxn 3005 using namespace std; int n, p, s; int a[maxn], b[maxn]; struct Edge { int to, nxt, w, dis; }edge[200010]; int h[maxn], cnt = 1, S, T; void addedge(int u, int v, int w, int dis) { ++ cnt; edge[cnt].to = v; edge[cnt].nxt = h[u]; edge[cnt].w = w; edge[cnt].dis = dis; h[u] = cnt; swap(u, v), dis = -dis, w = 0; ++ cnt; edge[cnt].to = v; edge[cnt].nxt = h[u]; edge[cnt].w = w; edge[cnt].dis = dis; h[u] = cnt; } const int inf = 0x7fffffff/2; queue<int> Q; int dis[maxn], pre[maxn], ans; bool vis[maxn]; bool spfa() { for(int i = S; i <= T; ++ i) dis[i] = -inf; dis[S] = 0; Q.push(S); while(!Q.empty()) { int u = Q.front(); Q.pop(); vis[u] = false; for(int i = h[u]; i; i = edge[i].nxt) { if(edge[i].w) { int v = edge[i].to; if(dis[v] < dis[u] + edge[i].dis) { dis[v] = dis[u] + edge[i].dis; pre[v] = i; if(!vis[v]) vis[v] = true, Q.push(v); } } } } return dis[T] > 0; } void solve() { int x = inf; for(int i = pre[T]; i; i = pre[edge[i^1].to]) x = min(x, edge[i].w); for(int i = pre[T]; i; i = pre[edge[i^1].to]) edge[i].w -= x, edge[i^1].w += x; ans += x*dis[T]; } void Maxcostflow() { while(spfa()) solve(); } int main() { scanf("%d%d%d", &n, &p, &s); for(int i = 1; i <= n; ++ i) scanf("%d", &a[i]); for(int i = 1; i <= n; ++ i) scanf("%d", &b[i]); S = 0, T = n+3; int A = n+1, B = n+2; for(int i = 1; i <= n; ++ i) { addedge(S, i, 1, 0); addedge(i, A, 1, a[i]); addedge(i, B, 1, b[i]); } addedge(A, T, p, 0); addedge(B, T, s, 0); Maxcostflow(); printf("%d\n", ans); vector<int> G; G.clear(); for(int i = h[A]; i; i = edge[i].nxt) if(edge[i].w == 1) G.push_back(edge[i].to); sort(G.begin(), G.end()); for(int i = 0; i < G.size(); ++ i) printf("%d ", G[i]); printf("\n"); G.clear(); for(int i = h[B]; i; i = edge[i].nxt) if(edge[i].w == 1) G.push_back(edge[i].to); sort(G.begin(), G.end()); for(int i = 0; i < G.size(); ++ i) printf("%d ", G[i]); return 0; }
J.Bottles
现在有n个瓶子,每个瓶子容量bi,已经装了ai的水
把所有的水转移到尽量少的瓶子至少需要几个瓶子?
满足上一问的情况下,最少转移多少单位的水?
dp[i][j][k]表示前i个瓶子选了j个瓶子已经装了k体积的水的最大不动的水的体积
#include <bits/stdc++.h> using namespace std; int n, need, sum, ans; int f[110][10010], a[110], b[110]; int main() { scanf("%d", &n); for(int i = 1; i <= n; ++ i) { scanf("%d", &a[i]); need += a[i]; } for(int i = 1; i <= n; ++ i) { scanf("%d", &b[i]); sum += b[i]; } memset(f, -1, sizeof f); f[0][0] = 0; for(int i = 1; i <= n; ++ i) for(int j = n; j >= 1; -- j) for(int k = sum; k >= b[i]; -- k) if(f[j-1][k-b[i]] != -1) f[j][k] = max(f[j][k], f[j-1][k-b[i]]+a[i]); ans = -1; for(int i = 1; i <= n; ++ i) { for(int j = need; j <= sum; ++ j) ans = max(ans, f[i][j]); if(ans != -1) { printf("%d %d\n", i, need-ans); break; } } return 0; }
2008-2009 ACM-ICPC, NEERC, Southern Subregional Contest
以下太难而且没有题解导致此比赛被弃坑回来再填吧
A题通过人数为0
点开比赛pdf并没有发现第一题是A题(纯属脑抽)
写了半天还以为题目很简单,看了样例发现自己题看错了(已扇自己一万次脸
那我就按照通过人数的顺序来了。。
G.Friends of Friends
最简单的一题,可惜还wa了,原因是没有sort(眼瞎看不懂英文increasing number)
#include <bits/stdc++.h> #define maxn 51 using namespace std; bool G[maxn][maxn], vis[maxn]; int n, x, y, num; int cnt, ans[maxn]; int main() { scanf("%d%d", &n, &x); for(int i = 1; i <= n; ++ i) { scanf("%d", &num); for(int j = 1; j <= num; ++ j) { scanf("%d", &y); if(i != y) G[i][y] = G[y][i] = 1; } } for(int i = 1; i <= n; ++ i) { if(G[x][i]) for(int j = 1; j <= n; ++ j) if(j != x && G[i][j] && !G[x][j] && !vis[j]) ans[++ cnt] = j, vis[j] = true; } if(cnt == 0) printf("0\n"); else { printf("%d\n", cnt); sort(ans+1, ans+1+cnt); for(int i = 1; i <= cnt; ++ i) printf("%d\n", ans[i]); } return 0; }
F.Text Editor
脑子年久失修,10^6想成了10^5
简单的链表,怪不得过得人这么多(放在考场上要罚多少时啊喂)
#include <bits/stdc++.h> #define maxn 1000010 using namespace std; char s[maxn], c[maxn]; int nxt[maxn], pre[maxn], cnt; int main() { scanf("%s", s+1); int len = strlen(s+1); int nw = 1; cnt = 1; for(int i = 1; i <= len; ++ i) { if(s[i] == 'L' && nw != 1) nw = pre[nw]; else if(s[i] == 'R' && nxt[nw]) nw = nxt[nw]; else if(s[i] >= 'a' && s[i] <= 'z'){ ++ cnt; c[cnt] = s[i]; if(nxt[nw]) { pre[nxt[nw]] = cnt; nxt[cnt] = nxt[nw]; } nxt[nw] = cnt; pre[cnt] = nw; nw = cnt; } } nw = 1; while(true) { nw = nxt[nw]; if(nw)putchar(c[nw]); else break; } return 0; }
未完待续。。