JZOJ 【2021.11.10NOIP提高組聯考】
阿新 • • 發佈:2021-11-10
簡要題解
這套題比較 \(H_2O\)
建議題目背景美文共賞
\(\text{T1}\)
顯然一個 \(O(n^3)\) 不能過的 \(dp\)
然而過了?!
用心在該卡時間的地方卡一卡
\(\text{Code}\)
#include <cstdio> #include <algorithm> #define re register using namespace std; typedef long long LL; const int N = 5005; int n, m, C, lim[N], cnt, col[N]; LL f[N][N], g[N]; struct node{int v, w, c;}a[N]; inline bool cmp(node a, node b){return a.c < b.c;} inline bool cmp1(node a, node b){return a.v > b.v;} inline void solve() { sort(a + 1, a + n + 1, cmp1); LL ans = 0; for(re int i = 1; i <= n; i++) { if (!m) break; if (lim[a[i].c]) ans += a[i].v, --lim[a[i].c], --m; } printf("%lld\n", ans); } int main() { freopen("diversity.in", "r", stdin), freopen("diversity.out", "w", stdout); scanf("%d%d%d", &n, &m, &C); int bz1 = 1, bz2 = 1; for(re int i = 1; i <= n; i++) scanf("%d%d%d", &a[i].v, &a[i].w, &a[i].c), bz2 = (bz2 && (a[i].w == 1)); sort(a + 1, a + n + 1, cmp); for(re int i = 1; i <= C; i++) scanf("%d", &lim[i]), bz1 = (bz1 && lim[i] >= m), lim[i] = (lim[i] > m ? m : lim[i]); if (bz2){solve(); return 0;} int r; for(re int l = 1; l <= n; l = r + 1) { r = l, col[++cnt] = a[l].c; while (r < n && a[r + 1].c == a[l].c) ++r; if (bz1) r = n; for(re int i = 1; i <= r - l + 1; i++) for(re int j = lim[a[l].c]; j >= a[i + l - 1].w; j--) f[cnt][j] = max(f[cnt][j], f[cnt][j - a[i + l - 1].w] + a[i + l - 1].v); } if (bz1){printf("%lld\n", f[1][m]); return 0;} for(re int i = 1; i <= cnt; i++) for(re int j = m; j >= 0; j--) for(re int k = 0; k <= min(j, lim[col[i]]); k++) g[j] = max(g[j], g[j - k] + f[i][k]); printf("%lld\n", g[m]); }
\(\text{T2}\)
二分答案,考慮如何 \(check\)
貪心的想,將 \(a\) 排序,一一對應要弄出的自然數序列 \(b\)
然後就是快速計算代價
考慮一個位置,前面的代價是 \(b_i - a_i\) 的形式,後面的是 \(a_i - b_i\) 的形式(連續性很明顯)
二分這個位置的話是 \(O(nlog^2n)\) 的,常數優秀就可以過
實際上這個分割的位置有單調性,指標處理即可
\(\text{Code}\)
#include <cstdio> #include <iostream> #include <algorithm> #define re register using namespace std; typedef long long LL; const int N = 1e6 + 5; int n; LL num, a[N], sum[N]; inline void read(int &x) { x = 0; char ch = getchar(); int f = 1; for(; !isdigit(ch); f = (ch == '-' ? -1 : f), ch = getchar()); for(; isdigit(ch); x = (x<<3) + (x<<1) + (ch^48), ch = getchar()); x *= f; } inline LL cost(int l, int r) { if (l > r) return 0; return 1LL * (r + l) * (r - l + 1) / 2; } inline int check(int mid) { LL res = num + 1; int r = mid + 1; for(re int i = 1; i <= n - mid; i++) { while (r && r - 1 < a[i + r - 1]) --r; res = min(res, cost(1, r - 1) - (sum[i + r - 1] - sum[i - 1]) + (sum[i + mid] - sum[i + r - 1]) - cost(r, mid)); } return res <= num; } int main() { freopen("dream.in", "r", stdin), freopen("dream.out", "w", stdout); read(n), scanf("%lld", &num); for(re int i = 1, x; i <= n; i++) read(x), a[i] = x; sort(a + 1, a + n + 1); for(re int i = 1; i <= n; i++) sum[i] = sum[i - 1] + a[i]; int l = 0, r = n - 1, mid, res = -1; while (l <= r) { mid = l + r >> 1; if (check(mid)) res = mid, l = mid + 1; else r = mid - 1; } printf("%lld", res + 1); }
\(\text{T3}\)
題目理解後就很好做了
考慮答案的暴力形式,莫反一下
發現是個數論分塊套數論分塊的形式
\(\text{Code}\)
#include <cstdio> #define re register #define LL long long using namespace std; const int N = 1e6, P = 998244353, inv2 = 499122177; int totp, n, k, T, pr[N], vis[N + 5], mu[N + 5], pw[N + 5]; inline int fpow(LL x, LL y) { if (x < k && pw[x]) return pw[x]; LL res = 1, xx = x; for(; y; y >>= 1, x = x * x % P) if (y & 1) res = res * x % P; if (xx < k) pw[xx] = res; return res; } inline void Euler() { vis[1] = mu[1] = 1; for(re int i = 2; i <= N; i++) { if (!vis[i]) pr[++totp] = i, mu[i] = -1; for(re int j = 1; j <= totp && i * pr[j] <= N; j++) { vis[i * pr[j]] = 1; if (!(i % pr[j])) break; mu[i * pr[j]] = -mu[i]; } } for(re int i = 1; i <= N; i++) mu[i] += mu[i - 1]; } inline LL F(int up) { int r; LL sum = 0; for(re int l = 1; l <= up; l = r + 1) { r = up / (up / l); sum = (sum + 1LL * (mu[r] - mu[l - 1] + P) % P * fpow(up / l, n) % P) % P; } return sum; } int main() { freopen("dance.in", "r", stdin), freopen("dance.out", "w", stdout); Euler(), scanf("%d", &T); for(; T; --T) { scanf("%d%d", &n, &k); for(re int i = 0; i <= k; i++) pw[i] = 0; LL ans = 0; int r; for(re int l = 1; l <= k; l = r + 1) { r = k / (k / l); ans = (ans + 1LL * (r + l) * (r - l + 1) % P * inv2 % P * F(k / l) % P) % P; } printf("%lld\n", ans * fpow(fpow(k, n), P - 2) % P); } }
\(\text{T4}\)
考慮一個座標向下走的貢獻
把一個座標能左右延伸出的位置處理出來,可單調棧做到 \(O(n)\) 也可懶惰的 \(O(n log n)\) 二分 + \(ST\) 表
然後把座標按 \(y\) 從小到大排序,掃過的點就滿足了 \(y\) 的限制
先處理當前點可延伸區間的貢獻再在樹狀陣列上加入這個點
非常之簡單
\(\text{Code}\)
#include <iostream>
#include <cstdio>
#include <algorithm>
#define re register
using namespace std;
typedef long long LL;
const int N = 5e5 + 5, P = 998244353;
int n, m, mx[N][21], lg[N];
struct point{int x, y, w, l, r;}p[N];
inline bool cmpy(point a, point b){return a.y < b.y;}
inline void read(int &x)
{
x = 0; char ch = getchar(); int f = 1;
for(; !isdigit(ch); f = (ch == '-' ? -1 : f), ch = getchar());
for(; isdigit(ch); x = (x<<3) + (x<<1) + (ch^48), ch = getchar());
x *= f;
}
inline int query(int l, int r)
{
int k = lg[r - l + 1];
return max(mx[l][k], mx[r - (1 << k) + 1][k]);
}
inline void prepare()
{
for(re int j = 1; j <= lg[n]; j++)
for(re int i = 1; i + (1 << j) - 1 <= n; i++)
mx[i][j] = max(mx[i][j - 1], mx[i + (1 << j - 1)][j - 1]);
for(re int i = 1; i <= m; i++)
{
int l = 1, r = p[i].x, mid;
while (l <= r)
{
mid = l + r >> 1;
if (query(mid, p[i].x) < p[i].y) p[i].l = mid, r = mid - 1;
else l = mid + 1;
}
l = p[i].x, r = n;
while (l <= r)
{
mid = l + r >> 1;
if (query(p[i].x, mid) < p[i].y) p[i].r = mid, l = mid + 1;
else r = mid - 1;
}
}
}
struct node{LL s0, s1, s2;};
inline node operator - (const node a, const node b)
{
return node{(a.s0 - b.s0 + P) % P, (a.s1 - b.s1 + P) % P, (a.s2 - b.s2 + P) % P};
}
struct BIT{
LL c0[N], c1[N], c2[N];
inline int lowbit(int x){return x & (-x);}
inline void add(int x, LL v)
{
for(; x <= n; x += lowbit(x))
++c0[x], c1[x] = (c1[x] + v) % P, c2[x] = (c2[x] + v * v % P) % P;
}
inline node getsum(int x)
{
int s0 = 0, s1 = 0, s2 = 0;
for(; x; x -= lowbit(x)) s0 = (s0 + c0[x]) % P, s1 = (s1 + c1[x]) % P, s2 = (s2 + c2[x]) % P;
return node{s0, s1, s2};
}
inline node query(int l, int r){return getsum(r) - getsum(l - 1);}
}T;
int main()
{
freopen("flame.in", "r", stdin), freopen("flame.out", "w", stdout);
read(n), read(m), lg[0] = -1;
for(re int i = 1; i <= n; i++) read(mx[i][0]), lg[i] = lg[i >> 1] + 1;
for(re int i = 1; i <= m; i++) read(p[i].x), read(p[i].y), read(p[i].w);
prepare(), sort(p + 1, p + m + 1, cmpy); node t; LL ans = 0;
for(re int i = 1; i <= m; i++)
{
t = T.query(p[i].l, p[i].r), T.add(p[i].x, p[i].w);
ans = (ans + t.s0 * p[i].w % P * p[i].w % P - t.s1 * 2 * p[i].w % P + t.s2 + P) % P;
}
printf("%lld\n", ans);
}