problem1 link
最优的策略就是从最低下一层开始,每两层的三个节点的子树都可以用一次遍历覆盖。
problem2 link
从大到小依次放置每一种数字,并记录已经放置的数字一共有多少个$m$以及有多少个严格的升序列$K$。那个如果新放置的一个数字(必然小于序列中所有的数字)放在了升序列的开头,那么升序列个数不变;否则升序列的个数增加1.所以有$K$个位置可以使得升序列个数不变,而有$n+1-K$个位置使得升序列个数增加1.现在考虑新放置的数字有多个的情况。假设新放置的数字有$n$个,首先将他们插入到原来的序列前现将其分为$t$组,要求每一组插入的时候是连续的。那么每一组中前面的数字必定是有几个就会使得升序列个数增加几个,即一定至少增加$n-t$.那么如果$t$组中选择$x$组放在之前增序列的开头,剩下的$t-x$组放在其他位置,那么增加的增序列的总个数为$(n-t)+(t-x)$,方案数为$C_{K}^{x}C_{m+1-K}^{t-x}$.
problem3 link
首先将边上的$4m$个点按照顺时针看作一个序列,那么对于序列中的每个点$i$可以计算一个$left[i],right[i]$。$left[i]$表示在$i$前面最远的点满足那个点与$i$点的连线使得所有内部的点都在这条线上或者右侧。$right[i]$含义类似。
现在枚举每个点作为$a$作为三角形的一个顶点,其他两个顶点设为$b,c$。那么$b,c$一定要满足$left[a]\leq c\leq right[b],left[c]\leq b\leq right[a]$.
所以如果$b$也一旦确定,那么$c$的可选个数为$right[b]-left[a]+1$,所以当$a$确定时的答案为$f(a)=\sum_{b=left[left[a]]}^{right[a]}right[b]-left[a]+1$
特殊情况是如果$left[left[a]]$跟 $a$是同一个点的时候,这时候$b$的取值是$[a+1, right[a]]$,即便这样$f(a)=\sum_{b=a+1}^{right[a]}right[b]-left[a]+1$还是多算了两个不合法的三角形,它们是 $(a,left[a],left[a])$以及$(a,left[a],a)$
code for problem1
#include <stdint.h>
class TrafficCongestion {
static constexpr int kMod = 1000000007;
public:
int theMinCars(int treeHeight) {
int result = 0;
while (treeHeight >= 0) {
if (treeHeight <= 1) {
result = (result + 1) % kMod;
break;
} else {
result += Pow(treeHeight - 1);
result %= kMod;
treeHeight -= 2;
}
}
return result;
}
private:
static int Pow(int k) {
int64_t result = 1;
int64_t a = 2;
while (k > 0) {
if ((k & 1) == 1) {
result = result * a % kMod;
}
a = a * a % kMod;
k >>= 1;
}
return static_cast<int>(result);
}
};
code for problem2
#include <stdint.h>
#include <cstring>
#include <vector>
class LISNumber {
static constexpr int64_t kMod = 1000000007;
static constexpr int64_t kMax = 36;
public:
int count(const std::vector<int> &cards, int K) {
if (K < cards.back()) {
return 0;
}
int N = 0;
for (auto e : cards) {
N += e;
}
if (K > N) {
return 0;
}
Initialize();
std::vector<int> f0(K + 1, 0);
std::vector<int> f1(K + 1, 0);
f0[cards.back()] = 1;
int m = cards.back();
for (int i = static_cast<int>(cards.size()) - 2; i >= 0; --i) {
int n = cards[i];
for (auto &e : f1) {
e = 0;
}
for (int k = 1; k <= K; ++k) {
if (f0[k] == 0) {
continue;
}
for (int t = 1; t <= n; ++t) {
auto b = static_cast<int64_t>(f0[k]) * split_[n][t] % kMod;
int delta_k = n - t;
for (int x = 0; x <= t && x + delta_k + k <= K; ++x) {
int b1 = static_cast<int>(b * c_[k][t - x] % kMod *
c_[m - k + 1][x] % kMod);
(f1[x + delta_k + k] += b1) %= kMod;
}
}
}
m += n;
f0 = f1;
}
return f0[K];
}
private:
void Initialize() {
c_[0][0] = 1;
for (int i = 1; i <= kMax; ++i) {
c_[0][i] = 0;
}
for (int i = 1, end = kMax * kMax; i <= end; ++i) {
c_[i][0] = 1;
for (int j = 1; j <= kMax; ++j) {
c_[i][j] = (c_[i - 1][j] + c_[i - 1][j - 1]) % kMod;
}
}
memset(split_, 0, sizeof(split_));
std::vector<std::pair<int, int>> p;
for (int i = 1; i <= kMax; ++i) {
p.push_back({i, 1});
Dfs(1, i, i, &p);
p.clear();
}
}
void Dfs(int depth, int begin, int sum, std::vector<std::pair<int, int>> *p) {
if (sum > kMax) {
return;
}
{
int64_t x = 1;
int n = depth;
for (size_t i = 0; i < p->size(); ++i) {
(x *= c_[n][(*p)[i].second]) %= kMod;
n -= (*p)[i].second;
}
(split_[sum][depth] += static_cast<int>(x)) %= kMod;
}
for (int i = begin, end = kMax - sum; i <= end; ++i) {
if (p->back().first == i) {
p->back().second += 1;
} else {
p->push_back({i, 1});
}
Dfs(depth + 1, i, sum + i, p);
if (p->back().second > 1) {
p->back().second -= 1;
} else {
p->pop_back();
}
}
}
int c_[kMax * kMax + 1][kMax + 1];
int split_[kMax + 1][kMax + 1];
};
code for problem3
#include <unistd.h>
#include <algorithm>
#include <vector>
class EnclosingTriangle {
public:
long long getNumber(int m, const std::vector<int>& x,
const std::vector<int>& y) {
const int n = m << 2;
std::vector<std::pair<int, int>> all(3 * n);
std::vector<int> left(3 * n);
std::vector<int> right(3 * n);
std::vector<long long> prefix(3 * n);
{
int idx = 0;
auto Add = [&](int x0, int y0, int x1, int y1) {
int dx = x1 >= x0 ? 1 : -1;
int dy = y1 >= y0 ? 1 : -1;
int x_num = abs(x1 - x0) + 1;
int y_num = abs(y1 - y0) + 1;
for (int i = 0; i < x_num; ++i) {
for (int j = 0; j < y_num; ++j) {
all[idx] = all[idx + n] = all[idx + n + n] =
std::make_pair(i * dx + x0, j * dy + y0);
++idx;
}
}
};
Add(0, 0, 0, m - 1);
Add(0, m, m - 1, m);
Add(m, m, m, 1);
Add(m, 0, 1, 0);
}
{
auto Check = [&](const std::pair<int, int>& p1,
const std::pair<int, int>& p2) {
long long dx1 = p2.first - p1.first;
long long dy1 = p2.second - p1.second;
for (size_t i = 0; i < x.size(); ++i) {
int dx2 = x[i] - p1.first;
int dy2 = y[i] - p1.second;
if (dx1 * dy2 - dy1 * dx2 > 0) {
return false;
}
}
return true;
};
for (int i = n; i < n + n; ++i) {
{
int low = i - n + 1;
int up = i - 1;
int result = up;
while (low <= up) {
int mid = (low + up) >> 1;
if (Check(all[mid], all[i])) {
result = std::min(result, mid);
up = mid - 1;
} else {
low = mid + 1;
}
}
left[i - n] = left[i] = left[i + n] = i - result;
}
{
int low = i + 1;
int up = i + n - 1;
int result = low;
while (low <= up) {
int mid = (low + up) >> 1;
if (Check(all[i], all[mid])) {
result = std::max(result, mid);
low = mid + 1;
} else {
up = mid - 1;
}
}
right[i - n] = right[i] = right[i + n] = result - i;
}
}
prefix[0] = right[0];
for (int i = 1; i < n + n + n; ++i) {
prefix[i] = prefix[i - 1] + right[i];
}
}
long long result = 0;
for (int a = n; a < n + n; ++a) {
int c = a - left[a];
int b_left = std::max(c - left[c] + n, a + 1);
int b_right = a + right[a];
if (b_right < b_left) {
continue;
}
result += prefix[b_right] - prefix[b_left - 1];
result -= static_cast<long long>(b_right - b_left + 1) *
(c + n - b_left - 1 + c + n - b_right - 1) / 2;
if (c + n == b_right) {
// (a, c, c) and (a, c, a) are invalid.
result -= 2;
}
}
return result / 3;
}
};