InfOJ NOIP2023 模拟赛
InfOJ NOIP2023 模拟赛
T1#
给定长度为
的数列 ,每次操作需要选择 ,满足 按位与的结果为 ,然后删去 ,删去后左边和右边合并起来。 问最多能合并多少次。
。
简单区间 dp。
设
转移就枚举中转点
时间复杂度
Code of T1
#include <bits/stdc++.h>
const int M = 70;
const int inf = 1e9 + 7;
int f[M][M][M];
int n, a[M];
int main() {
double st = clock();
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 0; i < M; i++)
for (int j = 0; j < M; j++)
for (int k = 0; k < M; k++) f[i][j][k] = -inf;
for (int i = 1; i <= n; i++)
if (a[i] == 0) f[i][i][63] = 1;
else f[i][i][a[i]] = 0;
for (int len = 2; len <= n; len++) {
for (int l = 1; l + len - 1 <= n; l++) {
int r = l + len - 1;
for (int m = l; m <= r; m++) {
for (int x = 0; x < 64; x++)
if (f[l][m][x] != -inf)
for (int y = 0; y < 64; y++)
if (f[m + 1][r][y] != -inf) {
f[l][r][x & y] = std::max(f[l][r][x & y], f[l][m][x] + f[m + 1][r][y]);
if ((x & y) == 0) f[l][r][63] = std::max(f[l][r][63], f[l][m][x] + f[m + 1][r][y] + 1);
}
}
int sum = a[l];
for (int i = l + 1; i <= r; i++) sum &= a[i];
f[l][r][sum] = std::max(f[l][r][sum], 0);
if (sum == 0) f[l][r][63] = std::max(f[l][r][63], 1);
}
}
int ans = 0;
for (int l = 1; l <= n; l++)
for (int r = l; r <= n; r++)
for (int x = 0; x < 64; x++) ans = std::max(ans, f[l][r][x]);
printf("%d\n", ans);
double ed = clock();
std::cerr << (int)(ed - st) << '\n';
return 0;
}
T2#
定义一个长度为
的数组 的不优美度为 。 给定数组
, 次询问,若有 次机会让某一个 执行 ,最小的不优美度是多少。
。
令
考虑如何减小不优美度。容易发现当
进一步地,如果有
但是每次只对
将询问的
因为有
Code of T2
#include <bits/stdc++.h>
using namespace std;
const int M = 5e5 + 10;
template<typename T>
inline T read() {
char ch = getchar();
T x = 0;
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
return x;
}
int n, q;
long long a[M];
pair<long long, int> x[M];
struct Node {
int l, r;
long long hgt;
bool operator > (const Node& o) const {
return r - l > o.r - o.l;
}
};
priority_queue<Node, vector<Node>, greater<Node> > Q;
struct BIT {
long long c[M];
void add(int x, long long v) {
for (; x <= n + 1; x += (x & -x)) c[x] += v;
}
void add(int l, int r, long long v) {
add(l, v), add(r + 1, -v);
}
long long operator[](int x) {
if (x < 1 || x > n) return 0;
long long ret = 0;
for (; x; x -= (x & -x)) ret += c[x];
return ret;
}
}h;
int L[M], R[M];
long long ans[M];
void Insert() {
for (int i = 1; i <= n; ) {
int cur = i;
while (cur < n && a[cur] == a[cur + 1]) cur++;
R[i] = cur, L[cur] = i;
if (a[cur] > a[cur + 1] && a[i] > a[i - 1]) Q.push({ i, cur, a[cur] });
i = cur + 1;
}
}
int main() {
double st = clock();
n = read<int>(), q = read<int>();
for (int i = 1; i <= n; i++) a[i] = read<long long>();
for (int i = 1; i <= q; i++) x[i].first = read<long long>(), x[i].second = i;
sort(x + 1, x + 1 + q);
long long sum = a[1] + a[n];
for (int i = 1; i < n; i++) sum += abs(a[i] - a[i + 1]);
for (int i = 1; i <= n; i++) h.add(i, a[i] - a[i - 1]);
Insert();
int curq = 0;
long long tot = 0;
while (x[curq + 1].first == 0 && curq < q) ans[x[++curq].second] = sum;
while (!Q.empty()) {
Node tp = Q.top(); Q.pop();
int len = tp.r - tp.l + 1;
long long hgt = std::max(h[tp.l - 1], h[tp.r + 1]);
long long size = (tp.hgt - hgt) * len;
h.add(tp.l, tp.r, hgt - tp.hgt);
while (curq < q && tot < x[curq + 1].first && x[curq + 1].first <= tot + size) {
curq++;
long long res = (x[curq].first - tot) / len;
ans[x[curq].second] = sum - res * 2;
}
tot += size, sum -= (tp.hgt - hgt) * 2;
int newL = tp.l, newR = tp.r;
if (tp.l > 1 && h[tp.l - 1] == hgt) newL = L[tp.l - 1];
if (tp.r < n && h[tp.r + 1] == hgt) newR = R[tp.r + 1];
R[newL] = newR, L[newR] = newL;
if (h[newL - 1] < hgt && h[newR + 1] < hgt) Q.push({ newL, newR, hgt });
}
while (curq < q) ans[x[++curq].second] = 0;
for (int i = 1; i <= q; i++) printf("%lld\n", ans[i]);
double ed = clock();
std::cerr << (int)(ed - st) << '\n';
return 0;
}
T3#
给定
个点的树,结点 为根。要用 种颜色对每个结点染色。每个结点有一个参数 表示 的子树恰好出现了 种颜色。如果 说明不限制子树颜色个数。问染色方案数。
。保证 。
考虑二项式反演。
设
根据
Code of T3
#include <bits/stdc++.h>
const int M = 2e5 + 10;
const int mod = 1e9 + 7;
int n, m, col[M];
std::vector<int> t[M];
int fac[M], facinv[M];
void calc() {
fac[0] = fac[1] = facinv[0] = facinv[1] = 1;
for (int i = 2; i < M; i++) fac[i] = 1ll * fac[i - 1] * i % mod;
for (int i = 2; i < M; i++) facinv[i] = 1ll * (mod - mod / i) * facinv[mod % i] % mod;
for (int i = 2; i < M; i++) facinv[i] = 1ll * facinv[i - 1] * facinv[i] % mod;
}
int C(int x, int y) {
if (x < y || x < 0 || y < 0) return 0;
return 1ll * fac[x] * facinv[y] % mod * facinv[x - y] % mod;
}
int siz[M], fa[M];
void dfs(int u, int f) {
siz[u] = 1, fa[u] = f;
for (int v : t[u]) {
if (v == f) continue;
dfs(v, u), siz[u] += siz[v];
}
}
namespace Sub1 {
int f[M], g[M][110];
void dp(int u) {
for (int i = 1; i <= m; i++) g[u][i] = i;
for (int v : t[u]) {
if (v == fa[u]) continue;
dp(v);
for (int i = 0; i <= m; i++)
if (col[v] != -1)
g[u][i] = 1ll * g[u][i] * f[v] % mod * C(i, col[v]) % mod;
else
g[u][i] = 1ll * g[u][i] * g[v][i] % mod;
}
for (int i = 0; i <= col[u]; i++) {
if ((col[u] - i) & 1)
(f[u] += mod - 1ll * C(col[u], i) * g[u][i] % mod) %= mod;
else
(f[u] += 1ll * C(col[u], i) * g[u][i] % mod) %= mod;
}
//printf("f[%d] = %d\n", u, f[u]);
}
void main() {
dp(1);
printf("%d\n", f[1]);
}
}
int main() {
calc();
scanf("%d%d", &n, &m);
for (int i = 2, fa; i <= n; i++)
scanf("%d", &fa), t[fa].push_back(i);
dfs(1, 0);
bool A = true;
for (int i = 1; i <= n; i++)
scanf("%d", &col[i]), A &= (col[i] != -1);
Sub1::main();
return 0;
}
T4#
给定长度为
的序列 ,有两种可执行的操作。 有
种形如 的操作,表示将 减一,将 加一,代价是 。 有
种形如 的操作,表示将 减一,代价是 。若 代表这个操作不可用。 问将
中所有元素变成 的最小代价。
。
先考虑一种特殊情况:只有
这时有两种选择,一种是直接使用
令
最后答案就是
Code of T4
#include <bits/stdc++.h>
const int M = 4e5 + 10;
const long long inf = 2e18 + 10;
int n, m, a[M], b[M];
int X[M], L[M], R[M], W[M];
int d[M];
int cnt[M << 2];
long long sum[M << 2];
std::vector<int> vec[M << 2];
void Insert(int k, int l, int r, int L, int R, int x) {
if (L <= l && r <= R) {
vec[k].push_back(x), d[x]++;
return;
}
int mid = (l + r) >> 1;
if (L <= mid) Insert(k << 1, l, mid, L, R, x);
if (R > mid) Insert(k << 1 | 1, mid + 1, r, L, R, x);
}
std::priority_queue<std::pair<long long, int>,
std::vector<std::pair<long long, int> >, std::greater<std::pair<long long, int> > > q;
long long cost[M], dis[M];
bool vis[M];
void Modify(int k, int l, int r, int p, long long v) {
cnt[k]++, sum[k] += v;
if (cnt[k] == r - l + 1)
for (int x : vec[k]) {
cost[x] += sum[k];
if (!(--d[x]))
if (dis[X[x]] > cost[x] + W[x]) {
dis[X[x]] = cost[x] + W[x];
if (!vis[X[x]]) q.push({ dis[X[x]], X[x] });
}
}
if (l == r) return;
int mid = (l + r) >> 1;
if (p <= mid) Modify(k << 1, l, mid, p, v);
else Modify(k << 1 | 1, mid + 1, r, p, v);
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= m; i++) {
scanf("%d%d%d%d", &X[i], &L[i], &R[i], &W[i]);
Insert(1, 1, n, L[i], R[i], i);
}
for (int i = 1; i <= n; i++) scanf("%d", &b[i]);
for (int i = 1; i <= n; i++) dis[i] = (b[i] == -1) ? inf : b[i];
for (int i = 1; i <= n; i++)
if (b[i] != -1) q.push({ b[i], i });
while (!q.empty()) {
auto tp = q.top(); q.pop();
if (vis[tp.second]) continue;
vis[tp.second] = true;
Modify(1, 1, n, tp.second, dis[tp.second]);
}
for (int i = 1; i <= n; i++)
if (dis[i] >= inf && a[i] > 0) return printf("-1\n"), 0;
long long ans = 0;
for (int i = 1; i <= n; i++) ans += dis[i] * a[i];
printf("%lld\n", ans);
return 0;
}
作者:zzxLLL
出处:https://www.cnblogs.com/zzxLLL/p/17828876.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话