模拟就好。
code
#include<bits/stdc++.h> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();} return x * f; } int n, now = 1, Ans; priority_queue<int, vector<int>, greater<int> >q; int main() { n = read(); for (int i = 1; i <= n; i++) { int x = read(); if(x == now) { now = x + 1; while(!q.empty() && (int)q.top() == now) { now++; q.pop(); } continue; } else { while(!q.empty() && (int)q.top() == now) { now++; q.pop(); } if(now == x) Ans = max(Ans, (int)q.size()); else { q.push(x); Ans = max(Ans, (int)q.size()); } } } cout<<Ans; return 0; }
打表找规律,然后大力分类讨论
code
#include<bits/stdc++.h> #define int long long using namespace std; const int MAXN = 1e5 + 5; int read() { int x = 0, f = 1; char c = getchar(); while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();} return x * f; } int n, m, a[MAXN]; struct node{int x, y;}b[MAXN]; signed main() { n = read(), m = read(); for (int i = 1; i <= n; i++) { int x = read(), y = read(); if(x == 0 && y == 0) a[i] = 0; int ret = abs(x) + abs(y); int cs = 1 + 2 * ret * (ret - 1); if (x > 0 && y == 0) a[i] = cs; if(x == 0 && y > 0) a[i] = cs + 2 * (ret - 1) + 1; int fx = cs + 2 * (ret - 1) + 1; if(x == 0 && y < 0) a[i] = cs + ret * 2 + (ret - 1) * 2 + 1; if(x > 0 && y > 0) a[i] = cs + (y - 1) * 2 + 1; if(x > 0 && y < 0) a[i] = cs + abs(y) * 2; if(x < 0 && y >= 0) a[i] = fx + abs(x); fx = fx + ret; if(x < 0 && y < 0) a[i] = fx + abs(y); } for (int i = 1; i <= m; i++) { int val = read(); if(val == 0) {b[i].x = 0, b[i].y = 0; continue;} int y = sqrt((val - 1) / 2), ret; ret = y; for (int j = max(y - 1, 0ll); j <= y + 30; j++) { if (1 + 2 * j * (j - 1) <= val && 1 + 2 * j * (j + 1) > val) {ret = j; break;} } int cs = 1 + 2 * ret * (ret - 1); if(val == cs) b[i].x = ret, b[i].y = 0; if(val >= cs + 1 && val <= cs + 2 * (ret - 1)) { int cz = val - cs; if(cz & 1) { b[i].x = ret - (cz / 2 + 1); b[i].y = cz / 2 + 1; } else { b[i].x = ret - cz / 2; b[i].y = (-1) * cz / 2; } } if(val == cs + (ret - 1) * 2 + 1) b[i].x = 0, b[i].y = ret; int fx = cs + (ret - 1) * 2 + 1; if(val >= fx + 1 && val <= fx + ret) { b[i].x = (-1) * (val - fx); b[i].y = b[i].x + ret; } fx = fx + ret; if(val >= fx + 1 && val <= fx + (ret - 1)) { b[i].y = (val - fx) * (-1); b[i].x = (-ret) - b[i].y; } if(val == fx + ret) { b[i].x = 0, b[i].y = (-1) * ret; } } for (int i = 1; i <= n; i++) cout<<a[i]<<"\n"; for (int i = 1; i <= m; i++) cout<<b[i].x<<" "<<b[i].y<<"\n"; return 0; }
solution
稍微把那个取最大值的条件转化一下,其实就是一种颜色只能取一次最大值。
有一个朴素的状压 dp:对于每个点 \(i\) 和集合 \(S\),\(dp[i][S]\) 代表到达 \(i\),在 \(S\) 中的点取过颜色,得到的最大路径长度。转移就是对于每条边 \(u\to v\),分别考虑要不要取 \(v\) 的权值。如果要取,那么必须之前没取过,也就是对于不包含 \(c_v\) 的集合 \(S\) 有 \(dp[v][S\cup\{c_v\}]\leftarrow dp[u][S]+a_v\)。如果不取就随意了,转移是 \(dp[v][S]\leftarrow dp[u][S]\)。这样复杂度是 \(m2^n\)。
然后你发现,如果一种颜色只出现了一次,显然没必要放到状压里,无脑取就行了
于是你只需要记出现至少两次的点,最多有 \(\frac n2\) 个。然后就做完了……时间复杂度 \(m2^{n/2}\)
code
#include<bits/stdc++.h> using namespace std; const int MAXN = 1e5 + 5; int read() { int x = 0, f = 1; char c = getchar(); while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();} return x * f; } int n, m, cnt[40], f[37][(1 << 18)], id[40], tot, val[40], col[40]; vector<int>e[40]; int main() { n = read(); for (int i = 1; i <= n; i++) { col[i] = read(), val[i] = read(); cnt[col[i]]++; if(cnt[col[i]] == 2) id[col[i]] = ++tot; } m = read(); for (int i = 1; i <= m; i++) { int u = read(), v = read(); e[v].push_back(u); } memset(f, -0x3f, sizeof f); if(cnt[col[1]] == 1) f[1][0] = val[1]; else { f[1][(1 << (id[col[1]] - 1))] = val[1]; f[1][0] = 0; } for (int i = 2; i <= n; i++) { for (int k = 0; k < e[i].size(); k++) { int j = e[i][k]; for (int S = 0; S < 1 << tot; S++) { if(cnt[col[i]] == 1) f[i][S] = max(f[i][S], f[j][S] + val[i]); else { f[i][S] = max(f[i][S], f[j][S]); int M = S | (1 << id[col[i]] - 1); if(!(S & (1 << id[col[i]] - 1))) f[i][M] = max(f[i][M], f[j][S] + val[i]); } } } } for (int i = 1; i <= n; i++) { int ans = *max_element(f[i], f[i] + (1 << tot)); if(ans < 0) ans = 0; cout<<ans<<"\n"; } system("pause"); return 0; }
solution
考虑 SA 中相邻的元素,也就是排名相邻的两个串。如果你知道 Height,相当于你就知道了 \(h_i\) 对字符的相等关系,和末尾一对字符的小于关系。对于相等关系,可以用并查集合并。
如果你不知道 Height,那也可以通过判断它后面的字符串的大小关系确定能不能相等。即,对于后缀 \(x,y\),\(x\) 的字典序小于 \(y\)。如果 \(x+1\) 字典序小于 \(y+1\),那么 \(x,y\) 位置的字符可以取等,否则必须 \(s_x<s_y\)。判断 \(x+1,y+1\) 的字典序可以通过求 SA 的逆数组得到。
然后把这些小于关系在并查集合并后的点上连边,那实际上就是找一个字典序最小拓扑序。可以按照 SA 的顺序贪心,每个合并后的点放能放的最小值,这样一定是字典序最小的(因为每个字符都是最小的)
code
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int, int> pii; typedef pair<ll, ll> pll; #define X first #define Y second #define ALL(v) v.begin(), v.end() #define pb push_back #define SZ(a) ((int)a.size()) const int MAXn = 5005; int sa[MAXn], hei[MAXn], rk[MAXn], val[MAXn]; int g[MAXn]; int f(int x) { return g[x] = (g[x] == x ? x : f(g[x])); } void mg(int a, int b) { a = f(a), b = f(b); g[a] = b; } vector<int> v[MAXn]; void add(int x, int y) { // x < y v[y].pb(x); } int main() { ios::sync_with_stdio(0), cin.tie(0); int n; cin >> n; for (int i = 1; i <= n; i++) cin >> sa[i], rk[sa[i]] = i; for (int i = 1; i < n; i++) cin >> hei[i]; for (int i = 1; i <= n + 1; i++) g[i] = i; rk[n + 1] = 0; for (int i = 1; i < n; i++) { if (hei[i] != -1) { for (int j = 0; j < hei[i]; j++) mg(sa[i] + j, sa[i + 1] + j); } } for (int i = 1; i < n; i++) { if (hei[i] != -1) { add(f(sa[i] + hei[i]), f(sa[i + 1] + hei[i])); } if (rk[sa[i] + 1] > rk[sa[i + 1] + 1]) add(f(sa[i]), f(sa[i + 1])); } val[n + 1] = -1; for (int i = 1; i <= n; i++) val[i] = -2; int cur = 0; for (int i = 1; i <= n; i++) { int t = f(sa[i]); if (val[t] != -2) { assert(val[t] == cur); continue; } for (ll x : v[t]) { assert(val[x] != -2); cur = max(cur, val[x] + 1); } assert(cur < 26); val[t] = cur; } for (int i = 1; i <= n; i++) cout << (char)('a' + val[f(i)]); cout << endl; }