我的第 100
场参与排名的 Codeforces
记录一下
A. Journey Planning
给一个长度为 \(n\) 的序列 \(b\),求子序列中满足原下标差与序列两项差相等的最大和
就是说有个序列 \(b\) ,求出一个子序列 \(x\) ,它在原序列中的下标依次为 \(c\),则对每个 \(i\) 都有
\(c_{i+1}-c_i = b_{c_{i+1}}-b_{c_i}\)
求 \(\sum x\) 的最大值
显然对于 \(i-b_i\) 相同的值都是可以放在同一个子序列中的,贪心丢进去就行了,图方便没有去判断正负直接用 \(map\) 储存差值即可
/*================================================================ * * 创 建 者: badcw * 创建日期: 2020/3/1 * ================================================================*/ #include <bits/stdc++.h> #define ll long long using namespace std; const int maxn = 2e5+50; const int mod = 1e9+7; ll qp(ll a, ll n) { ll res = 1; while (n > 0) { if (n & 1) res = res * a % mod; a = a * a % mod; n >>= 1; } return res; } template <class T> inline bool scan(T& ret) { char c; int sgn; if (c = getchar(), c == EOF) return 0; // EOF while (c != '-' && (c < '0' || c > '9')) c = getchar(); sgn = (c == '-') ? -1 : 1; ret = (c == '-') ? 0 : (c - '0'); while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0'); ret *= sgn; return 1; } //template <class T> //inline void out(T x) { // if (x > 9) out(x / 10); // putchar(x % 10 + '0'); //} int n; map<int, ll> mp; int main(int argc, char* argv[]) { scanf("%d", &n); vector<int> a(n); for (int i = 0; i < n; ++i) scanf("%d", &a[i]); for (int i = 0; i < n; ++i) { mp[a[i] - i] += a[i]; } ll res = 0; for (auto i : mp) { if (i.second > res) res = i.second; } printf("%lld\n", res); return 0; }
B. Navigation System
给一张有向图,边权全 1
,给出行走路径,已知所有信息,求最少和最多需要改变多少次最短路径导航(也就是每次在一个点给出到终点的导航,但是实际路径没有按导航走,到下个点时换了另一条路)
Q:为什么会有多种情况?A:多条最短路的时候可能选择其中任何一条,需要分别求最小和最大值
数据范围:点、边、行走路径长度最多 \(2\cdot 10^5\)
首先考虑到换路问题需要当前点到终点的最短路,所以反向建图对终点求一次单源 dij
假设现在在 \(x\) 点,下一个点是 \(y\),如果 \(dist[x]=dist[y]+1\) 说明 \(x\to y\) 是 \(x\to last\) 的最短路(可能是之一)
然后遍历 \(x\) 所有的后继节点(正向图)发现没有其他满足与 \(y\) 一样条件的点的话就是唯一最短路,那么两个答案都不变。如果有多条最短路只把最多次数 +1 即可。如果不是最短路就是两个答案都 +1
/*================================================================ * * 创 建 者: badcw * 创建日期: 2020/3/1 * ================================================================*/ #include <bits/stdc++.h> #define ll long long using namespace std; const int maxn = 2e5 + 50; const int mod = 1e9 + 7; ll qp(ll a, ll n) { ll res = 1; while (n > 0) { if (n & 1) res = res * a % mod; a = a * a % mod; n >>= 1; } return res; } template<class T> inline bool scan(T &ret) { char c; int sgn; if (c = getchar(), c == EOF) return 0; // EOF while (c != '-' && (c < '0' || c > '9')) c = getchar(); sgn = (c == '-') ? -1 : 1; ret = (c == '-') ? 0 : (c - '0'); while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0'); ret *= sgn; return 1; } //template <class T> //inline void out(T x) { // if (x > 9) out(x / 10); // putchar(x % 10 + '0'); //} int n, m, k; int dis[maxn]; vector<int> edge[maxn], inv[maxn]; void dijkstra(int s) { priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > que; memset(dis, 0x3f, sizeof dis); que.push({0, s}); dis[s] = 0; while (!que.empty()) { auto f = que.top(); que.pop(); int u = f.second, d = f.first; if (d != dis[u]) continue; for (auto v : edge[u]) { if (dis[u] + 1 < dis[v]) { dis[v] = dis[u] + 1; que.push({dis[v], v}); } } } } int main(int argc, char *argv[]) { scanf("%d%d", &n, &m); for (int i = 0, u, v; i < m; ++i) { scanf("%d%d", &u, &v); edge[v].push_back(u); inv[u].push_back(v); } scanf("%d", &k); vector<int> path(k); for (int i = 0; i < k; ++i) { scanf("%d", &path[i]); } dijkstra(path.back()); int mnres = 0, mxres = 0; for (int i = 0; i < k - 1; ++i) { int mn = 0x3f3f3f3f, count = 0; for (auto v : inv[path[i]]) { if (dis[v] == mn) { count ++; } else if (dis[v] < mn) { mn = dis[v]; count = 1; } } if (mn == dis[path[i + 1]] && count == 1) ; else if (mn == dis[path[i + 1]] && count > 1) mxres ++; else if (mn < dis[path[i + 1]]) mnres ++, mxres ++; // cerr << mn << " " << dis[path[i + 1]] << endl; } printf("%d %d\n", mnres, mxres); return 0; }
C. World of Darkraft: Battle for Azathoth
\(n\) 个武器 \(m\)个防具(weapons
and armor
总感觉有点熟悉??某游戏mod制作者发出了鸽子的声音)
武器有攻击力,防具有防御力,每件装备都有它的花费
\(p\) 个怪物,每个都有其攻击力、防御力、价值
现在需要挑一件武器和一件防具,对于 \(p\) 个怪物,如果你选择的武器攻击力大于它的防御力、防具大于它的攻击力,则可以无损地获得它的价值。求最大价值 - 花费
\(1 \leq n, m, p \leq 2 \cdot 10^5\)
看到这种数据范围和两个能力值就知道是二维点对问题了
很套路的扫描线
假设一个二维空间 \(xoy\),\(x\) 轴表示攻击力轴,\(y\) 轴表示防御力轴(当然也可以反过来),每个怪物是离散的二维点,那么对于一组武器、防具的选择则有左下方的所有点都可以击败,也就是一个二维带权区间最值,总之对防具(\(y\) 轴)排序,对武器(\(x\) 轴)建立线段树求区间和,扫描线往上增加怪物的价值点求最大值即可。
具体来说,每个点的权值为:首先第一次将线段树上的所有点减去武器的花费,然后枚举每一个防具,最大值 - 它的花费就是答案。所有防具枚举完取最大值就是最后的答案。实际上每个怪物对大于它防御的武器都是有贡献的所以需要区间加、区间求最值。
我好啰嗦
/*================================================================ * * 创 建 者: badcw * 创建日期: 2020/3/1 * ================================================================*/ #include <bits/stdc++.h> #define ll long long using namespace std; const int maxn = 2e5+50; const int mod = 1e9+7; int n, m, p; int val[maxn << 2], lazy[maxn << 2]; int a[maxn], ca[maxn]; int vala[maxn]; inline void build(int rt, int l, int r) { if (l == r) { val[rt] = -vala[l]; return; } int mid = l + r >> 1; build(rt << 1, l, mid); build(rt << 1 | 1, mid + 1, r); val[rt] = max(val[rt << 1], val[rt << 1 | 1]); } inline void pushup(int rt) { val[rt] = max(val[rt << 1], val[rt << 1 | 1]); } inline void pushdown(int rt) { if (lazy[rt]) { lazy[rt << 1] += lazy[rt]; lazy[rt << 1 | 1] += lazy[rt]; val[rt << 1] += lazy[rt]; val[rt << 1 | 1] += lazy[rt]; lazy[rt] = 0; } } int le, k; inline void update(int rt, int l, int r) { if (le <= l) { val[rt] += k; lazy[rt] += k; return; } int mid = l + r >> 1; pushdown(rt); if (le <= mid) update(rt << 1, l, mid); update(rt << 1 | 1, mid + 1, r); pushup(rt); } struct node { int x, y, z; bool operator < (const node& oth) const { return y < oth.y; } }; vector<int> xpos; int main(int argc, char* argv[]) { scanf("%d%d%d", &n, &m, &p); xpos.resize(n); for (int i = 1; i <= n; ++i) { scanf("%d%d", &a[i], &ca[i]); xpos[i - 1] = a[i]; } sort(xpos.begin(), xpos.end()); xpos.erase(unique(xpos.begin(), xpos.end()), xpos.end()); for (int i = 1; i <= n; ++i) vala[i] = 0x3f3f3f3f; for (int i = 1; i <= n; ++i) { int pos = lower_bound(xpos.begin(), xpos.end(), a[i]) - xpos.begin() + 1; vala[pos] = min(vala[pos], ca[i]); } build(1, 1, n); vector<pair<int, int> > b(m); for (int i = 0; i < m; ++i) { scanf("%d%d", &b[i].first, &b[i].second); } sort(b.begin(), b.end()); vector<node> x(p); for (int i = 0; i < p; ++i) { scanf("%d%d%d", &x[i].x, &x[i].y, &x[i].z); } sort(x.begin(), x.end()); int now = 0; ll res = -0x3f3f3f3f3f3f3f3f; for (int i = 0; i < m; ++i) { ll tmp = -b[i].second; while (now < p) { if (x[now].y < b[i].first) { le = upper_bound(xpos.begin(), xpos.end(), x[now].x) - xpos.begin() + 1; k = x[now].z; if (le <= n) update(1, 1, n); now ++; } else break; } tmp += val[1]; // cerr << tmp << " " << i << endl; res = max(res, tmp); } printf("%lld\n", res); return 0; }
D. Reachable Strings
给一个串,长度不超过 \(2 \cdot 10^5\) ,\(2 \cdot 10^5\) 次询问两个等长的子串 \(s1,s2\)
是否可达
所谓 \(s,t\) 可达
即是指对于串 \(s\) 任意次将子串 011
替换成 110
或者 110
替换成 011
能变为 \(t\)
首先,显然两个串的 0 的数量相等、1 的数量相等
其次,显然两个串的独立 1 相对位置不变,that's to say 010
can't be 100
or 001
那么就可以认定,在去掉所有相邻的一对 1 之后如果串相同则认为两个串可达
赛中其实是想到了结论的,然而写的实在是太丑了
线段树维护所有不相邻 1 的 hash 即可
很久没写过这么复杂的线段树了,合并属实难写,以至于我想一句句的说明它每句的意思
但还是算了,实在太麻烦了,总之维护区间左值(pflag)、右值(sflag)、已经删除了连续 1 的对数(del)、删除之后的长度(len)、删除之后的 hash 值(hs)即可
注意此处有可能需要删掉 hash 值的最后一个值和开头一个值,稍作分析就知道一定会产生除法,所以必须取模使用逆元。另外一提此题有其他简单写法,不过我没有看懂别人按奇偶分类的 hash 写法
/*================================================================ * * 创 建 者: badcw * 创建日期: 2020/3/4 * ================================================================*/ #include <bits/stdc++.h> #define ll long long using namespace std; const int maxn = 2e5+50; const int mod = 1e9+7; ll qp(ll a, ll n) { ll res = 1; while (n > 0) { if (n & 1) res = res * a % mod; a = a * a % mod; n >>= 1; } return res; } template <class T> inline bool scan(T& ret) { char c; int sgn; if (c = getchar(), c == EOF) return 0; // EOF while (c != '-' && (c < '0' || c > '9')) c = getchar(); sgn = (c == '-') ? -1 : 1; ret = (c == '-') ? 0 : (c - '0'); while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0'); ret *= sgn; return 1; } //template <class T> //inline void out(T x) { // if (x > 9) out(x / 10); // putchar(x % 10 + '0'); //} int n, q; char s[maxn]; const int prim = 997; const int invs = qp(prim, mod - 2); ll base[maxn] = {1}; struct node { int len; int del; ll hs; bool pflag, sflag; bool operator == (const node& oth) const { return len == oth.len && del == oth.del && hs == oth.hs && pflag == oth.pflag && sflag == oth.sflag; } }p[maxn << 2]; node comb(node a, node b) { if (!a.len) { b.del += a.del; return b; } if (!b.len) { a.del += b.del; return a; } node ret; ret.del = a.del + b.del; if (a.sflag && b.pflag) { ret.del ++; a.hs = (a.hs - 1 + mod) * invs % mod; a.len --; a.sflag = 0; if (a.len == 0) a.pflag = 0; b.hs = (b.hs - base[b.len - 1] + mod) % mod; b.len --; b.pflag = 0; if (b.len == 0) b.sflag = 0; ret.len = a.len + b.len; ret.pflag = (a.len == 0 ? b.pflag : a.pflag); ret.sflag = (b.len == 0 ? a.sflag : b.sflag); ret.hs = (a.hs * base[b.len] % mod + b.hs) % mod; } else { ret.len = a.len + b.len; ret.hs = (a.hs * base[b.len] % mod + b.hs) % mod; ret.pflag = (a.len == 0 ? b.pflag : a.pflag); ret.sflag = (b.len == 0 ? a.sflag : b.sflag); } return ret; } void build(int rt, int l, int r) { if (l == r) { p[rt].len = 1; p[rt].del = 0; p[rt].hs = s[l] - '0'; p[rt].sflag = p[rt].pflag = s[l] - '0'; return; } int mid = l + r >> 1; build(rt << 1, l, mid); build(rt << 1 | 1, mid + 1, r); p[rt] = comb(p[rt << 1], p[rt << 1 | 1]); } int le, re; node query(int rt, int l, int r) { if (le <= l && r <= re) { return p[rt]; } int mid = l + r >> 1; node tmp{0, 0, 0, 0, 0}; if (le <= mid) { tmp = comb(tmp, query(rt << 1, l, mid)); } if (re > mid) { tmp = comb(tmp, query(rt << 1 | 1, mid + 1, r)); } return tmp; } int main(int argc, char* argv[]) { for (int i = 1; i < maxn; ++i) base[i] = 1ll * base[i - 1] * prim % mod; scanf("%d%s%d", &n, s + 1, &q); build(1, 1, n); for (int i = 0; i < q; ++i) { int l1, l2, len; scanf("%d%d%d", &l1, &l2, &len); le = l1, re = l1 + len - 1; node p1 = query(1, 1, n); le = l2, re = l2 + len - 1; node p2 = query(1, 1, n); puts(p1 == p2 ? "YES" : "NO"); } return 0; }
来源:https://www.cnblogs.com/badcw/p/12424412.html