首先离散化,并将相邻的相同的数变成一个。考虑对值域从小到大DP,那么如果 i i 和 i+1 i + 1 之间没有被切开那么 i+1 i + 1 和 i+2 i + 2 之间可能就必须切开,所以只需要维护DP值和上次决策点即可。不难发现只维护最大值和次大值就可以转移了。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
scanf("%d", &n);
vector<int> a(n);
for (int i = 0; i < n; ++i) {
scanf("%d", &a[i]);
}
vector<int> disc = a;
sort(disc.begin(), disc.end());
disc.erase(unique(disc.begin(), disc.end()), disc.end());
vector<vector<int>> all(disc.size() + 1);
vector<int> b;
for (int i = 0; i < n; ++i) {
a[i] = lower_bound(disc.begin(), disc.end(), a[i]) - disc.begin();
if (!b.empty() && a[i] == b.back()) {
continue;
}
all[a[i]].push_back(b.size());
b.push_back(a[i]);
}
n = b.size();
pair<int, int> u = make_pair(0, n), v = make_pair(0, n);
for (int i = 0; i < disc.size(); ++i) {
pair<int, int> x = make_pair(u.first, n), y = make_pair(v.first, n);
for (auto p : all[i]) {
if (p != n - 1 && b[p] + 1 == b[p + 1]) {
pair<int, int> value(1 + (u.second + 1 != p ? u.first : v.first), all[i + 1].size() == 1 ? n : p);
y = max(y, value);
if (x < y) {
swap(x, y);
}
}
}
u = x;
v = y;
}
printf("%d\n", n - 1 - u.first);
return 0;
}
特判 n≤3 n ≤ 3 的情况,否则每次可以把boss可能的位置减少 1 1 ,答案是 n−2 n − 2 。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
scanf("%d", &n);
printf("%d\n", max(n - 2, 1));
return 0;
}
贪心,维护当前合法的角度区间即可。
#include
using namespace std;
struct angle_t {
int x, y, turn;
angle_t(int x = 0, int y = 0, int turn = 0):x(x), y(y), turn(turn) {
}
angle_t operator - (const angle_t &b) const {
return angle_t(x - b.x, y - b.y, turn);
}
int where() const {
if (y < 0) {
if (x >= 0) {
return 3;
} else {
return 2;
}
} else if (y > 0) {
if (x <= 0) {
return 1;
} else {
return 0;
}
} else {
if (x <= 0) {
return 2;
} else {
return 0;
}
}
}
angle_t rotate() {
return angle_t(-x, -y, turn + (where() >= 2));
}
bool operator < (const angle_t &b) const {
if (turn != b.turn) {
return turn < b.turn;
} else if (where() != b.where()) {
return where() < b.where();
} else {
return x * b.y > y * b.x;
}
}
bool operator <= (const angle_t &b) const {
return !(b < *this);
}
bool operator > (const angle_t &b) const {
return b < *this;
}
bool operator >= (const angle_t &b) const {
return !(*this < b);
}
};
struct range_t {
angle_t l, r;
bool tl, tr;
range_t() {
}
range_t(angle_t angle) {
l = r = angle;
tl = tr = true;
}
bool in(angle_t angle) {
while (!(tl ? l <= angle : l < angle)) {
++angle.turn;
}
return tr ? angle <= r : angle < r;
}
};
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n = 4, answer = 1;
vector a(n * n);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
int x;
scanf("%d", &x);
a[x - 1] = angle_t(i, j, 0);
}
}
range_t current(a[1] - a[0]);
for (int i = 2; i < n * n; ++i) {
angle_t delta = a[i] - a[i - 1];
if (current.in(delta)) {
current = range_t(delta);
} else {
++answer;
angle_t l = current.l.rotate(), r = current.r.rotate();
while (r > delta.rotate()) {
--r.turn;
}
while (r.rotate() <= delta) {
++r.turn;
}
while (l >= delta.rotate()) {
--l.turn;
}
while (l.rotate() < delta) {
++l.turn;
}
current = range_t(delta);
if (r > delta) {
current.r = r;
current.tr = false;
}
if (l < delta) {
current.l = l;
current.tl = false;
}
int diff = -current.l.turn;
current.l.turn += diff;
current.r.turn += diff;
}
}
printf("%d\n", answer);
return 0;
}
模拟。
#include
using namespace std;
typedef long long ll;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(0);
cin.tie(0);
int n;
cin >> n;
vector<string> a(n);
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
int m;
cin >> m;
map<string, int> correct, incorrect;
map<string, string> translation;
while (m--) {
string from, to, state;
cin >> from >> to >> state;
if (state == "correct") {
++correct[from];
} else {
++incorrect[from];
}
translation[from] = to;
}
ll ways = 1, ok = 1;
for (int i = 0; i < n; ++i) {
ways *= (correct[a[i]] + incorrect[a[i]]);
ok *= correct[a[i]];
}
if (ways == 1) {
for (int i = 0; i < n; ++i) {
cout << translation[a[i]] << (i == n - 1 ? '\n' : ' ');
}
cout << (ok ? "correct" : "incorrect") << endl;
} else {
cout << ok << " correct" << endl;
cout << ways - ok << " incorrect" << endl;
}
return 0;
}
将桌子按照容量排序,并在最后新增 t t 个容量为 ∞ ∞ 的桌子。最后人一定是坐满了若干个区间,首先预处理 f[l][r] f [ l ] [ r ] 表示只坐了区间 [l,r] [ l , r ] 的桌子的方案数和答案总和,转移枚举最后一个坐的桌子 mid m i d ,两边是子问题,而最后一次来的人数一定在区间 (al−1,amid] ( a l − 1 , a m i d ] 内,就可以转移了。
从后往前DP,记 g[i][j] g [ i ] [ j ] 表示考虑 i i 之后的时刻,当前区间最左端点是 j j 的方案数和答案总和,转移的时候枚举上一次转移的时刻和上一次区间的开始位置,用前缀和优化即可。
#include
using namespace std;
typedef long double ld;
pair operator + (pair a, pair b) {
return make_pair(a.first + b.first, a.second + b.second);
}
pair operator * (pair a, pair b) {
return make_pair(a.first * b.second + a.second * b.first, a.second * b.second);
}
int sum(int x) {
return x * (x + 1) >> 1;
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n, group, hours;
scanf("%d %d %d", &n, &group, &hours);
vector<int> a(n);
for (int i = 0; i < n; ++i) {
scanf("%d", &a[i]);
a[i] = min(a[i], group);
}
sort(a.begin(), a.end());
for (int i = 0; i < hours; ++i) {
a.push_back(group + 1);
++n;
}
vector<vector > binom(n + 1);
for (int i = 0; i <= n; ++i) {
binom[i].push_back(1);
for (int j = 1; j <= i; ++j) {
binom[i].push_back((j != i ? binom[i - 1][j] : 0) + binom[i - 1][j - 1]);
}
}
vector<vector >> dp(n, vector > (n));
for (int j = 0; j < n; ++j) {
for (int i = j; ~i; --i) {
for (int k = i; k <= j; ++k) {
pair value = make_pair(0, binom[j - i][k - i]);
value = value * make_pair(a[k] == group + 1 ? 0 : sum(a[k]) - sum(i ? a[i - 1] : 0), min(a[k], group) - (i ? min(a[i - 1], group) : 0));
if (k != i) {
value = value * dp[i][k - 1];
}
if (k != j) {
value = value * dp[k + 1][j];
}
dp[i][j] = dp[i][j] + value;
}
}
}
vector<vector >> f(hours, vector > (n)), sum(hours, vector > (n));
for (int i = 0; i < hours; ++i) {
for (int j = 0; j <= n + i - hours; ++j) {
f[i][j] = dp[j][j + hours - i - 1];
}
}
for (int i = hours - 1; ~i; --i) {
for (int j = n - 1; ~j; --j) {
for (int k = i + 1; k < hours; ++k) {
if (j + k - i + 1 < n) {
pair value = make_pair(0, binom[hours - i][hours - k]);
value = value * dp[j][j + k - i - 1] * sum[k][j + k - i + 1];
f[i][j] = f[i][j] + value;
}
}
sum[i][j] = f[i][j];
if (j + 1 < n) {
sum[i][j] = sum[i][j] + sum[i][j + 1];
}
}
}
pair answer;
for (int i = 0; i < n; ++i) {
answer = answer + f[0][i];
}
printf("%.8lf\n", (double)(answer.first / answer.second));
return 0;
}
分解质因数求出每个向左向右最远到哪都和它互质,按中序遍历考虑整个序列,维护一个栈表示之前的最右链,每次新增一个点的时候先往下挂,然后试图往上,直到父亲的 r r 大于自己的 r r 或者它的 l l 不能覆盖左边的子树。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
scanf("%d", &n);
vector<int> a(n);
for (int i = 0; i < n; ++i) {
scanf("%d", &a[i]);
}
int m = *max_element(a.begin(), a.end());
vector<int> min_prime(m + 1), primes;
for (int i = 2; i <= m; ++i) {
if (!min_prime[i]) {
min_prime[i] = i;
primes.push_back(i);
}
for (auto p : primes) {
if (i * p > m) {
break;
}
min_prime[i * p] = p;
if (i % p == 0) {
break;
}
}
}
vector<int> l(n), r(n), last(m + 1, -1);
for (int i = 0; i < n; ++i) {
l[i] = -1;
int x = a[i];
while (x != 1) {
int p = min_prime[x];
l[i] = max(l[i], last[p]);
last[p] = i;
while (x % p == 0) {
x /= p;
}
}
++l[i];
}
for (int i = 0; i <= m; ++i) {
last[i] = n;
}
for (int i = n - 1; ~i; --i) {
r[i] = n;
int x = a[i];
while (x != 1) {
int p = min_prime[x];
r[i] = min(r[i], last[p]);
last[p] = i;
while (x % p == 0) {
x /= p;
}
}
--r[i];
}
vector<int> answer(n);
vectorint , int>, int>> stack(n + 1);
stack[0].first.second = -1;
int top = 0;
for (int i = 0; i < n; ++i) {
stack[top + 1].first.second = -1;
int temp = i;
while (top && l[i] <= stack[top].first.first && r[i] >= stack[top].second) {
temp = min(temp, stack[top].first.first);
--top;
}
if (~stack[top + 1].first.second) {
answer[stack[top + 1].first.second] = i;
}
answer[i] = stack[top].first.second;
int right = top ? stack[top].second : n;
if (right < i) {
puts("impossible");
return 0;
}
stack[++top] = make_pair(make_pair(temp, i), min(r[i], right));
}
for (int i = 0; i < n; ++i) {
printf("%d%c", answer[i] + 1, i == n - 1 ? '\n' : ' ');
}
return 0;
}
枚举边数,可以推公式算出两个bound,直接计算就行了。
#include
using namespace std;
typedef long long ll;
typedef long double ld;
const ld pi = acosl(-1);
const ld inf = 1e9;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
scanf("%d", &n);
vector<int> x(n), y(n);
for (int i = 0; i < n; ++i) {
scanf("%d %d", &x[i], &y[i]);
}
ld answer = 0;
int best = -1;
for (int i = 3; i <= 8; ++i) {
ld inner = inf, outer = 0;
for (int j = 0; j < n; ++j) {
ld angle = atan2(y[j], x[j]);
while (angle < 0) {
angle += 2 * pi / i;
}
while (angle >= 2 * pi / i) {
angle -= 2 * pi / i;
}
ld dist = sqrtl((ll)x[j] * x[j] + (ll)y[j] * y[j]) * cosl(angle - pi / i);
inner = min(inner, dist);
outer = max(outer, dist);
}
ld ratio = inner / outer;
ratio *= ratio;
if (ratio > answer) {
answer = ratio;
best = i;
}
}
printf("%d %.8lf\n", best, (double)answer);
return 0;
}
平方增长较快,所以枚举较小的两个增加了多少,这个值不会太大,暴力即可。
#include
using namespace std;
typedef long long ll;
ll calc(int a, int b, int c) {
return (ll)a * a + (ll)b * b + (ll)c * c + 7ll * min(a, min(b, c));
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int T;
scanf("%d", &T);
while (T--) {
int a, b, c, d;
scanf("%d %d %d %d", &a, &b, &c, &d);
ll answer = 0;
for (int i = 0; i <= d && i <= 100; ++i) {
for (int j = 0; i + j <= d && j <= 100; ++j) {
int k = d - i - j;
answer = max(answer, calc(a + i, b + j, c + k));
answer = max(answer, calc(a + i, b + k, c + j));
answer = max(answer, calc(a + k, b + i, c + j));
}
}
printf("%lld\n", answer);
}
return 0;
}
考虑如果所有都选,最优顺序一定是按照 max(d,s)−d max ( d , s ) − d 从大到小排序,然后记 f(i,j) f ( i , j ) 表示考虑前 i i 个物品,选了 j j 个,最少占用多少空间。
#include
using namespace std;
struct info_t {
int need, get, id;
info_t(int need = 0, int get = 0, int id = 0):need(need), get(get), id(id) {
}
bool operator < (const info_t &b) const {
return get > b.get;
}
};
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n, size;
scanf("%d %d", &n, &size);
vector apps(n);
for (int i = 0; i < n; ++i) {
int disk, storage;
scanf("%d %d", &disk, &storage);
apps[i] = info_t(max(disk, storage), max(disk, storage) - storage, i);
}
sort(apps.begin(), apps.end());
vector<vector<int>> f(n + 1, vector<int> (n + 1, size + 1));
vector<vector<bool>> g(n + 1, vector<bool> (n + 1, false));
f[0][0] = 0;
for (int i = 0; i < n; ++i) {
for (int j = 0; j <= i; ++j) {
f[i + 1][j] = f[i][j];
}
for (int j = 0; j <= i; ++j) {
if (f[i][j] + apps[i].need <= size && f[i + 1][j + 1] > f[i][j] + apps[i].need - apps[i].get) {
f[i + 1][j + 1] = f[i][j] + apps[i].need - apps[i].get;
g[i + 1][j + 1] = true;
}
}
}
for (int i = n; ~i; --i) {
if (f[n][i] <= size) {
printf("%d\n", i);
if (i) {
vector<int> answer;
for (int j = n, current = i; j; --j) {
if (g[j][current]) {
--current;
answer.push_back(apps[j - 1].id);
}
}
reverse(answer.begin(), answer.end());
for (int j = 0; j < answer.size(); ++j) {
printf("%d%c", answer[j] + 1, j == answer.size() - 1 ? '\n' : ' ');
}
}
return 0;
}
}
return 0;
}
每个 2 2 单独考虑,假设它是 i i ,求出它左右的 0 0 的位置 l,r l , r ,那么就是将 l,r l , r 赋为 1 1 ,将 l+r−i l + r − i 赋为 0 0 。用set维护即可。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(0);
cin.tie(0);
string str;
cin >> str;
int n = str.length();
set<int> s;
s.insert(-1);
s.insert(n);
for (int i = 0; i < n; ++i) {
if (str[i] == '0') {
s.insert(i);
}
}
for (int i = 0; i < n; ++i) {
if (str[i] == '2') {
auto l = s.lower_bound(i), r = l;
--l;
int x = *l + *r - i;
if (~*l) {
s.erase(l);
}
if (*r < n) {
s.erase(r);
}
s.insert(x);
}
}
string answer(n, '1');
for (auto p : s) {
if (p >= 0 && p < n) {
answer[p] = '0';
}
}
cout << answer << endl;
return 0;
}
将 a1 a 1 认为是 0 0 ,按照 a a 从小到大排序,相邻的匹配最优。然后做个树形DP即可。
#include
using namespace std;
typedef long double ld;
void dfs(int x, int m, int &index, vector<vectorint , ld>>> &f) {
if ((x << 1 | 1) >= m) {
f[x].push_back(make_pair(--index, 1));
} else {
dfs(x << 1 | 1, m, index, f);
dfs(x + 1 << 1, m, index, f);
}
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
scanf("%d", &n);
vector<int> a(n);
for (int i = 0; i < n; ++i) {
scanf("%d", &a[i]);
}
int first = a[0];
a[0] = 0;
sort(a.begin(), a.end());
a[0] = first;
int m = (n << 1) - 1, binary = 1;
while ((binary << 1 | 1) <= m) {
binary = binary << 1 | 1;
}
vector<vectorint , ld>>> f(m);
int index = n;
dfs(0, m, index, f);
for (int i = m - n - 1, j = 0; ~i; --i) {
int l = i << 1 | 1, r = i + 1 << 1;
for (int rotate = 0; rotate < 2; ++rotate) {
for (auto p : f[l]) {
int index = p.first;
ld prob = 0;
for (auto q : f[r]) {
prob += q.second * a[index] / (a[index] + a[q.first]);
}
f[i].push_back(make_pair(index, prob * p.second));
}
swap(l, r);
}
}
for (auto p : f[0]) {
if (!p.first) {
printf("%.8lf\n", (double)p.second);
}
}
return 0;
}