CLYZ-NOIP十連測 Day3
阿新 • • 發佈:2021-10-03
掛100分
CLYZ-NOIP2021 國慶集訓 B 組 Day3
題面:https://files.cnblogs.com/files/blogs/575626/day3B.zip
頭文字 A
我們考慮記下來到 \(n\) 的答案為 \(a_n\),那麼我們可以得到一個顯然的遞推式
\[a_n=a_{n-1}+a_{n-2}+n-2 \]然後這玩意直接用矩陣快速冪轉移即可。
#include <bits/stdc++.h> const int mod = 1e9 + 7; inline int Mod(int x) { if (x >= mod) { return x - mod; } else { return x; } } using std::cin; using std::cout; int n; struct Matrix { int a[4][4]; Matrix() { memset(a, 0, sizeof a); } Matrix operator * (Matrix b) { Matrix ret; for (int i = 0; i < 4; ++i) { for (int k = 0; k < 4; ++k) { for (int j = 0; j < 4; ++j) { ret.a[i][j] = (ret.a[i][j] + (long long) a[i][k] * b.a[k][j]) % mod; } } } return ret; } Matrix operator ^ (int x) { Matrix ret; for (int i = 0; i < 4; ++i) { ret.a[i][i] = 1; } Matrix u = *this; for (; x; x >>= 1, u = u * u) { if (x & 1) { ret = ret * u; } } return ret; } }; int main() { freopen("subset.in", "r", stdin); freopen("subset.out", "w", stdout); cin >> n; Matrix I; I.a[0][0] = 1; I.a[0][1] = 1; I.a[0][2] = 1; I.a[1][0] = 1; I.a[2][2] = 1; I.a[2][3] = 1; I.a[3][3] = 1; if (n < 3) { cout << 0 << '\n'; return 0; } I = I ^ (n - 2); cout << Mod(I.a[0][2] + I.a[0][3]) << '\n'; return 0; }
頭文字 B
這玩意,很明顯首先求一個強連通分量,縮點,然後求的就是剩下的 \(DAG\) 上的最長鏈。
#include <bits/stdc++.h> using std::cin; using std::vector; using std::pair; using std::cout; const int N = 1e6 + 10; int n, m, dfc, dfn[N], low[N], num, col[N], top, stk[N], in[N], val[N], f[N]; vector<int> v[N], e[N]; template<typename T> inline T min(const T &x, const T &y) { return x > y ? y : x; } template<typename T> inline T max(const T &x, const T &y) { return x > y ? x : y; } void dfs(int u) { dfn[u] = low[u] = ++dfc; stk[++top] = u; for (auto &j: v[u]) { if (dfn[j]) { if (!col[j]) { low[u] = min(low[u], dfn[j]); } } else { dfs(j); low[u] = min(low[u], low[j]); } } if (low[u] == dfn[u]) { ++num; while (!col[u]) { col[stk[top--]] = num; val[num]++; } } return; } int main() { freopen("bomb.in", "r", stdin); freopen("bomb.out", "w", stdout); std::ios::sync_with_stdio(0); cin.tie(0); cin >> n >> m; for (int i = 1, x, y; i <= m; ++i) { cin >> x >> y; v[x].push_back(y); } for (int i = 1; i <= n; ++i) { if (!dfn[i]) { dfs(i); } } for (int i = 1; i <= n; ++i) { for (auto &j: v[i]) { if (col[j] != col[i]) { e[col[j]].push_back(col[i]); in[col[i]]++; } } } std::queue<int> q; for (int i = 1; i <= num; ++i) { if (!in[i]) { q.push(i); f[i] = val[i]; } } int ans = 0; while (q.size()) { int nw = q.front(); q.pop(); ans = max(ans, f[nw]); for (auto &j: e[nw]) { f[j] = max(val[j] + f[nw], f[j]); if (!--in[j]) { q.push(j); } } } cout << ans << '\n'; return 0; }
頭文字 C
我們考慮這玩意肯定是底邊最短的時候,層數最多。
那麼考慮設計一個 \(dp\) 表示, \(f_i\) 表示 \([i...n]\) 中最下層的最短長度。
然後 \(f_i=\min_{j=i+1 \and sum_{j-1}-sum_{i-1}\ge f_j}^n sum_{j-1}-sum_{i-1}\)
這玩意的話,考慮直接用單調佇列維護即可。
#include<bits/stdc++.h> const int N = 100005; int n, s[N], f[N], g[N], q[N], h, t; int main() { freopen("block.in", "r", stdin); freopen("block.out", "w", stdout); scanf("%d", &n); h = t = 1; q[1] = n + 1; for (int i = 1, x; i <= n; i++) { scanf("%d", &x); s[i] = s[i - 1] + x; } for (int i = n; i >= 1; i--) { while (h < t && s[q[h + 1] - 1] - f[q[h + 1]] >= s[i - 1]) { h++; } f[i] = s[q[h] - 1] - s[i - 1]; g[i] = g[q[h]] + 1; while (h <= t && s[i - 1] - f[i] >= s[q[t] - 1] - f[q[t]]) { t--; } q[++t] = i; } printf("%d\n", g[1]); return 0; }
頭文字 D
暴力想法,過了。
我們考慮,暴力維護這些東西,考慮維護一個數組 \(val_i\) 這個東西肯定是單調不增的,然後連續的段很多。維護這些連續段,考慮每次新增一個數會對於這玩意產生什麼影響即可,就是列舉這個數的因數,看他之前在哪裡出現,然後出現的位置到 \(i\) 這個區間裡取 \(\max\) 即可。
這個取 \(max\) 操作最後可以用一個類似於歸併操作的trick轉化為 \(O(段數+d(a_i))\) 做一次。
最後列舉所有段數求答案即可。
時間複雜度看起來像是 \(n\log^2 n\) 的
#include <bits/stdc++.h>
using std::cin;
using std::cout;
using std::vector;
using std::tuple;
using std::make_tuple;
using std::__gcd;
using std::get;
using std::pair;
using std::make_pair;
using std::cerr;
template<typename T>
inline T max(const T &x, const T &y) {
return x > y ? x : y;
}
template<typename T>
inline T min(const T &x, const T &y) {
return x < y ? x : y;
}
const int N = 1e5 + 10;
int n, a[N], pos[N];
long long cnt[N];
vector<int> d[N];
vector<tuple<int, int, int>> v;
int main() {
freopen("gcd.in", "r", stdin);
freopen("gcd.out", "w", stdout);
std::ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
for (int i = 1; i <= n; ++i) {
for (int j = i; j <= n; j += i) {
d[j].emplace_back(i);
}
}
for (auto &j: d[a[1]]) {
pos[j] = 1;
}
for (int i = 2; i <= n; ++i) {
vector<tuple<int, int, int>> tmp, tmp1;
vector<pair<int, int>> v1;
for (int j = 0; j < d[a[i]].size(); ++j) {
if (pos[d[a[i]][j]]) {
v1.emplace_back(pos[d[a[i]][j]], d[a[i]][j]);
}
}
std::sort(v1.begin(), v1.end(), std::greater<pair<int, int>> ());
int last = i, mx = 0;
for (auto &j: v1) {
if (j.first < last && j.second > mx) {
if (last != i) {
tmp.emplace_back(mx, j.first + 1, last);
}
last = j.first;
mx = j.second;
}
}
v.insert(v.begin(), make_tuple(__gcd(a[i], a[i - 1]), i - 1, i - 1));
tmp.emplace_back(mx, 1, last);
int I = 0, J = 0;
while (I < v.size() && J < tmp.size()) {
if (get<0>(v[I]) >= get<0>(tmp[J])) {
int mx = std::max(get<1>(v[I]), get<1>(tmp[J]));
tmp1.emplace_back(get<0>(v[I]), mx, get<2>(v[I]));
if (mx == get<1>(v[I])) {
++I;
}
else {
get<2>(v[I]) = mx - 1;
}
if (mx == get<1>(tmp[J])) {
++J;
}
else {
get<2>(tmp[J]) = mx - 1;
}
}
else {
int mx = std::max(get<1>(v[I]), get<1>(tmp[J]));
tmp1.emplace_back(get<0>(tmp[J]), mx, get<2>(tmp[J]));
if (mx == get<1>(v[I])) {
++I;
}
else {
get<2>(v[I]) = mx - 1;
}
if (mx == get<1>(tmp[J])) {
++J;
}
else {
get<2>(tmp[J]) = mx - 1;
}
}
}
v.clear();
for (int i = 0; i < tmp1.size(); ++i) {
if (!v.size()) {
v.push_back(tmp1[i]);
}
else if (get<0>(v.back()) == get<0>(tmp1[i])) {
get<1>(v.back()) = get<1>(tmp1[i]);
}
else {
v.push_back(tmp1[i]);
}
}
for (auto &j: v) {
cnt[get<0>(j)] += get<2>(j) - get<1>(j) + 1;
}
for (auto &j: d[a[i]]) {
pos[j] = i;
}
}
for (int i = 1; i <= n; ++i) {
cout << cnt[i] << '\n';
}
return 0;
}