训练赛地址ios
题目连接
⭐⭐⭐c++
题意:
有一个长为\(L\)的停车道,坐标从0开始,车辆停放时车头朝向正方向,且距离车头\(f\)范围内和距离车尾\(b\)范围内不能有其它车辆。
有两种操做:数组
解析:函数
#include<bits/stdc++.h> using namespace std; /*===========================================*/ struct Tree { int l, r, lazy, ls, rs, mx; }; const int maxn = 1e6 + 205; struct Segment_Tree { Tree tree[maxn << 2]; void pushdown(int root) { if (~tree[root].lazy) { tree[root << 1].mx = tree[root << 1].ls = tree[root << 1].rs = (tree[root << 1].r - tree[root << 1].l) * tree[root].lazy; tree[root << 1 | 1].mx = tree[root << 1 | 1].ls = tree[root << 1 | 1].rs = (tree[root << 1 | 1].r - tree[root << 1 | 1].l) * tree[root].lazy; tree[root << 1].lazy = tree[root << 1 | 1].lazy = tree[root].lazy; tree[root].lazy = -1; } } void pushup(int root) { tree[root].ls = tree[root << 1].ls, tree[root].rs = tree[root << 1 | 1].rs; tree[root].mx = max(tree[root << 1].mx, tree[root << 1 | 1].mx); tree[root].mx = max(tree[root].mx, tree[root << 1].rs + tree[root << 1 | 1].ls); if (tree[root << 1].mx == tree[root << 1].r - tree[root << 1].l) tree[root].ls += tree[root << 1 | 1].ls; if (tree[root << 1 | 1].mx == tree[root << 1 | 1].r - tree[root << 1 | 1].l) tree[root].rs += tree[root << 1].rs; } void build(int k, int l, int r) { tree[k].l = l, tree[k].r = r; tree[k].ls = tree[k].rs = tree[k].mx = r - l; tree[k].lazy = -1; if (r - l != 1) build(k << 1, l, l + (r - l) / 2), build(k << 1 | 1, l + (r - l) / 2, r); } void update(int k, int ql, int qr, int val) { int& l = tree[k].l, & r = tree[k].r, & mx = tree[k].mx, & ls = tree[k].ls, & rs = tree[k].rs; if (ql <= l && r <= qr) { ls = rs = mx = (r - l) * val; tree[k].lazy = val; } else if (ql<r && qr>l) { pushdown(k); update(k << 1, ql, qr, val); update(k << 1 | 1, ql, qr, val); pushup(k); } } int query(int k, int val) { int& l = tree[k].l, & r = tree[k].r, & mx = tree[k].mx, & ls = tree[k].ls, & rs = tree[k].rs; if (tree[k << 1].mx >= val) { pushdown(k); return query(k << 1, val); } else if (tree[k << 1].rs + tree[k << 1 | 1].ls >= val) return tree[k << 1].r - tree[k << 1].rs; else if (tree[k << 1 | 1].mx >= val) { pushdown(k); return query(k << 1 | 1, val); } return -1; } }sol; pair<int, int> qu[105]; int main() { //freopen("abc.in", "r", stdin); int L, b, f; int n, opt, a; while (~scanf("%d%d%d", &L, &b, &f)) { L += b + f; sol.build(1, 0, L); scanf("%d", &n); for (int i = 1; i <= n; ++i) { scanf("%d%d", &opt, &a); if (opt == 1) { int ans = sol.query(1, a + b + f); printf("%d\n", ans); if (ans == -1) continue; qu[i].first = ans + b; qu[i].second = qu[i].first + a; sol.update(1, qu[i].first, qu[i].second, 0); } else sol.update(1, qu[a].first, qu[a].second, 1); } } }
同时须要保存对应的操做数,操做结束后进行排序
3. 遇到2操做,\(O(n)\)查询\(a\)所对应的车辆,进行删除便可ui
#include<bits/stdc++.h> using namespace std; /*===========================================*/ struct Car { int id; int l, r; bool operator<(const Car& a)const { return l < a.l; } }; vector<Car> car; void solve() { int L, b, f, n; while (~scanf("%d%d%d", &L, &b, &f)) { car.clear(); car.push_back(Car{ -1, -b, -b }); car.push_back(Car{ -1,L + f,L + f }); int opt, a; scanf("%d", &n); for (int j = 1; j <= n; ++j) { scanf("%d%d", &opt, &a); if (opt == 1) { bool ok = false; for (int i = 1; i < car.size(); ++i) { if (car[i].l - car[i - 1].r >= a + b + f) { car.push_back(Car{ j,car[i - 1].r + b,car[i - 1].r + b + a }); printf("%d\n", car[car.size() - 1].l); ok = true; sort(car.begin(), car.end()); break; } } if (!ok) printf("-1\n"); } else for (int i = 1; i < car.size(); ++i) if (car[i].id == a) { car.erase(car.begin() + i); break; } } } } int main() { solve(); }
题目连接
⭐⭐编码
题意:
给你三个整数,\(n,d,k\)。你的任务是从新构建出一棵树或者判断出不能构造出这样的树。\(n\)表明这棵树一共有\(n\)个点,\(d\)表明的是这棵树的直径为\(d\),\(k\)是每一个最大节点度数为\(k\)。若是可以构造出这样的树,输出‘YES’,和全部的边。若是不能,那么输出‘NO’。spa
解析:.net
注意:code
#include<cstdio> #include<algorithm> #include<vector> using namespace std; int now; typedef pair<int, int> P; vector<P> v; int n, d, k; void dfs(int fa, int up, int lim) { if (now == n || !up || !lim) return; while (up--) { v.push_back(P(fa, ++now)); if (now == n) return; else dfs(now, k - 1, lim - 1); if (now == n) return; } } int main() { scanf("%d%d%d", &n, &d, &k); if (k == 1 && n > 2 || n <= d) { printf("NO"); return 0; } bool ok = false; now = d + 1; for (int i = 1; i <= d; ++i) v.push_back(P(i, i + 1)); for (int i = 2; i <= d; ++i) dfs(i, k - 2, min(i - 1, d + 1 - i)); if (now == n) { printf("YES\n"); for (auto& i : v) printf("%d %d\n", i.first, i.second); } else printf("NO"); }
题目连接
⭐⭐排序
题目:
给出\(n\)个数,从中选出长度为\(k\)的子序列造成环,若是相邻两个元素之和不超过\(M\),求解\(M_{min}\)
解析:
注意:
#include<cstdio> #include<algorithm> using namespace std; const int maxn=2e5+5; int w[maxn]; int p[maxn]; int n,k; int ok(long long m) { int res=0; int cnt=0; for(int i=0;i<n;++i) if(w[i]<=m/2) p[cnt++]=i; if(cnt<=1) return cnt; for(int i=1;i<cnt;++i) for(int j=p[i-1]+1;j<p[i];++j) if(1ll*w[j]+max(w[p[i-1]],w[p[i]])<=m) { ++res; break; } for(int j=p[cnt-1]+1;j<n;++j) if(1ll*w[j]+max(w[p[cnt-1]],w[p[0]])<=m) return res+cnt+1; for(int j=0;j<p[0];++j) if(1ll*w[j]+max(w[p[cnt-1]],w[p[0]])<=m) return res+cnt+1; return res+cnt; } int main() { scanf("%d%d",&n,&k); for(int i=0;i<n;++i) scanf("%d",&w[i]); long long l=0,r=2e9+10,mid; while(l<r) { mid=l+(r-l)/2; if(ok(mid)>=k) r=mid; else l=mid+1; } printf("%lld",r); }
题目连接
⭐
题意:
给出¥1,¥5,¥10,¥50,四种钞票,问\(n\)张钞票能够组成多少种不一样的面额
解析:
初看题目很像一道dp,但发现\(n\)高达\(10^9\),果断放弃
打表找规律不失为一种好方法
注意:不开long long成神仙
总结:
经过打表发如今\(n>11\)时,答案是一个等差数列,公差为49,这样的话对\(n\le11\)部分进行特判,就解决了问题
打表代码:
#include<bits/stdc++.h> using namespace std; /*===========================================*/ set<int> s; void dfs(int x, int cur, int num) { switch (x) { case 0: for (int i = 0; i <= cur; ++i) dfs(x + 1, cur - i, num + i); break; case 1: for (int i = 0; i <= cur; ++i) dfs(x + 1, cur - i, num + 5 * i); break; case 2: for (int i = 0; i <= cur; ++i) dfs(x + 1, cur - i, num + 10 * i); break; case 3: s.insert(num + cur * 50); } }
#include<bits/stdc++.h> using namespace std; /*===========================================*/ LL ans[] = { 0,4,10,20,35,56,83,116,155,198,244,292 }; int main() { LL n; scanf("%lld", &n); if (n <= 11) printf("%lld", ans[n]); else printf("%lld", ans[11] + 49 * (n - 11)); }
题目连接
⭐⭐⭐
题意:
给出\(n\)个独立的单词。在任意两个相邻的单词之间有且仅有一个空格。须要注意的是,在第一个单词以前没有空格,在最后一个单词以后也没有空格。
如今能够进行一次操做,将重复出现过的单词以及他们之间的空格进行组合,造成一个单词组,每一个单词用一个字母表示,例如"\(ab\ cd\ ab\ cd\ ee\rightarrow A A\ ee\)",这个单词组在原所给单词序列中出现须要多于一次
问进行这样的操做后,求单词序列长度的最小值
解析:
能够发现减小了\((\sum_{i=1}^nsz_i)+n-1\),而增长了单词数量所对应的表明字符\(n\),因此总的来讲减小了\((\sum_{i=1}^nsz_i)-1\),对结果统计最小值便可
#include<bits/stdc++.h> #define IOS std::ios::sync_with_stdio(0);cin.tie(0); using namespace std; /*===========================================*/ map<string, int> m; const int maxn = 305; int sz[maxn]; int a[maxn]; int nxt[maxn]; int cnt; int main() { IOS int n, res = 0; string s; cin >> n; for (int i = 0; i < n; ++i) { cin >> s; if (!m[s]) m[s] = ++cnt, sz[cnt] = s.size(); a[i] = m[s]; res += s.size(); } res += n - 1; int total = res; for (int lf = 0; lf < n; ++lf) { int len = 0; for (int ri = lf; ri < n; ++ri) { len += sz[a[ri]]; nxt[0] = -1; int cnt = 0; int i = 0, k = -1; while (i < ri - lf) { if (k == -1 || a[lf + i] == a[lf + k]) { if (a[lf + ++i] == a[lf + ++k]) // 当两个字符相等时要跳过 nxt[i] = nxt[k]; else nxt[i] = k; } else k = nxt[k]; } nxt[ri - lf + 1] = 0; i = 0; // 主串的位置 int j = 0; // 模式串的位置 while (i < n) { if (j == -1 || a[i] == a[lf + j]) { ++i, ++j; if (j == ri - lf + 1) { ++cnt; j = nxt[j]; //操做 } } else j = nxt[j]; } if (cnt != 1) res = min(res, total - cnt * (len - 1)); } } cout << res; }