T1009 数位的关系(HDU 7441)

考虑 \(l = r\) 的情况,此时只要计算一个数字,我们将其展开为一个字符串 \(S\)。设 \(f_{i, j, k}\) 表示考虑了 \(S\) 的前 \(i\) 位,选出了 \(j\) 个数字加入子序列,最后一个加入子序列的数字是 \(k\) 的方案数,转移平凡。

拓展到 \(l \neq r\),经典地将答案差分为 \(f(r + 1) - f(l)\),其中 \(f(i)\) 表示 \([1, i)\) 中的答案。设 \(g_{i, j, k, 0/1}\) 表示任意填 \(i\) 位,选出子序列的 \([j, |R| + 1]\) 部分,最后一个数字为 \(k\),是否(\(0/1\))能填 \(0\) 的方案数。

其他就是数位 \(\text{dp}\) 套路了,假设要算 \(f(x)\),\(x\) 作为字符串长度为 \(len\)。不足 \(len\) 位答案可直接用 \(g\) 表示;恰好 \(len\) 位枚举第一个 \(<x\) 的位置 \(i\),第 \([1, i)\) 位必须和 \(x\) 相同,答案可用 \(f\) 表示,第 \(i\) 位在 \([0, x_i)\) 中枚举,第 \([i + 1, len]\) 位任意填,答案可用 \(g\) 表示,三段拼起来即可。

Code
#include <iostream>
#include <cstring>
#include <vector> using namespace std; const int N = 505;
const int Mod = 998244353; int R, dig[N];
string rs;
int f[N][N][10][10], g[2][N][N][10]; bool valid (int x, char o, int y) {
if (o == '=') return x == y;
if (o == '<') return x < y;
return x > y;
} int dp (int i, int j, int k, int l) {
if (!j) return k == R;
if (g[i][j][k][l] != -1) return g[i][j][k][l];
int res = 0;
for (int d = !i; d <= 9; ++d) {
if (!k || valid(l, rs[k], d)) {
res = (res + dp(1, j - 1, k + 1, d)) % Mod;
}
res = (res + dp(1, j - 1, k, l)) % Mod;
}
return g[i][j][k][l] = res;
} int calc_pre (string s) {
int len = s.size();
string tmp = s;
for (int i = 1; i <= len; ++i) {
dig[i] = tmp.back() - '0';
tmp.pop_back();
}
int res = 0;
for (int i = 1; i < len; ++i) {
res = (res + dp(0, i, 0, 0)) % Mod;
}
s = " " + s;
s[0] = 0;
for (int i = 1; i <= len; ++i) {
s[i] -= '0';
}
memset(f, 0, sizeof(f));
f[0][0][0][0] = 1;
for (int i = 1; i <= len; ++i) {
for (int d = 0; d <= 9; ++d) {
for (int j = 0; j <= R; ++j) {
for (int k = 0; k <= 9; ++k) {
f[i][j][k][d] = (f[i][j][k][d] + f[i - 1][j][k][s[i - 1]]) % Mod;
if (j < R && (!j || valid(k, rs[j], d))) {
f[i][j + 1][d][d] = (f[i][j + 1][d][d] + f[i - 1][j][k][s[i - 1]]) % Mod;
}
}
}
}
}
for (int i = len; i; --i) {
for (int j = (i == len); j < dig[i]; ++j) {
for (int k = 0; k <= R; ++k) {
for (int l = 0; l <= 9; ++l) {
res = (res + 1ll * f[len - i + 1][k][l][j] * dp(1, i - 1, k, l)) % Mod;
}
}
}
}
return res;
} void add (string &s) {
int len = s.size();
for (int i = len - 1; ~i; --i) {
if (s[i] < '9') {
++s[i];
break;
}
else {
s[i] = '0';
}
}
if (s[0] == '0') s = "1" + s;
} int main () {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int T;
cin >> T;
while (T--) {
string l, r;
cin >> l >> r >> rs, add(r);
R = rs.size() + 1, rs = " " + rs;
memset(g, -1, sizeof(g));
cout << (calc_pre(r) - calc_pre(l) + Mod) % Mod << '\n';
}
return 0;
}

T1010 众数(HDU 7442)

注意到数据随机,我们猜测答案极大概率是大数,考虑套用 P5926 [JSOI2009] 面试的考验 的做法,取前 \(D = 600\) 大的数,算一个数对一次询问的贡献。

维护当前未被删除的数构成的连续段,每次删除一个未被删除的最大数 \(x\) 并算其贡献。考虑 \(x\) 所在连续段中任意一个区间对 \(x\) 都有贡献,将连续段的限制和询问区间的限制取交,方案数好算。

现在删除了前 \(D\) 大的数,对于每个询问初步得到了一个 \(ans\),我们想知道这个询问是否可能出现更优的解。我们将这个询问覆盖的连续段拿出来(连续段数 \(O(D)\),可以接受),和询问区间取交,设连续段取交后长度为 \(len_{1 \sim m}\),若 \(\sum \limits_{i = 1}^{m} \frac{len_i(len_i + 1)}{2} > ans\) 则说明这个询问在可能有更优解,单调栈线性暴力即可。

由于数据随机,根据前面的猜想,我们认为需要暴力的区间不会很多。

时间复杂度 \(O(Dn + ?)\),其中 \(?\) 是需要暴力的区间的总长度,最坏情况下(数据可不随机) \(? = O(n^2)\),但算法的正确性可保证。

Code
#include <iostream>
#include <stack>
#include <unordered_map>
#include <vector>
#include <algorithm> using namespace std;
using Vec = vector<int>;
using LL = long long; const int N = 2e5 + 5, D = 600; int n, q;
int a[N], L[N], R[N], mx[N];
int ql[N], qr[N];
LL mp[N], tmpc[N], mxc[N];
vector<int> vec[N], now; int query (int l, int r) {
stack<int> s;
fill(L + l, L + r + 1, l - 1);
fill(R + l, R + r + 1, r + 1);
for (int i = l; i <= r; ++i) {
while (!s.empty() && a[i] > a[s.top()]) {
R[s.top()] = i;
s.pop();
}
if (!s.empty()) L[i] = s.top();
s.push(i);
}
for (int i = l; i <= r; ++i) {
mp[a[i]] += 1ll * (i - L[i]) * (R[i] - i);
}
int val = 0;
LL mx = -1;
for (int i = l; i <= r; ++i) {
if (mp[a[i]] > mx || mp[a[i]] == mx && a[i] > val) {
val = a[i], mx = mp[val];
}
mp[a[i]] = 0;
}
return val;
} int main () {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int T;
cin >> T;
while (T--) {
cin >> n >> q;
fill(vec + 1, vec + n + 1, vector<int>()), now.clear();
fill(mx + 1, mx + q + 1, 0);
fill(mxc + 1, mxc + q + 1, 0);
for (int i = 1; i <= n; ++i) {
cin >> a[i];
vec[a[i]].push_back(i);
}
for (int i = 1; i <= q; ++i) {
cin >> ql[i] >> qr[i];
}
for (int i = n, c = 0; i; --i) {
if (vec[i].empty()) continue;
if (c + vec[i].size() > D) break;
c += vec[i].size();
fill(tmpc + 1, tmpc + q + 1, 0);
for (int x = 0, y = 0; x < vec[i].size(); ++x) {
int pos = vec[i][x];
while (y < now.size() && now[y] < pos) {
++y;
}
int tL = !y ? 0 : now[y - 1], tR = n + 1;
if (y != now.size()) tR = min(tR, now[y]);
if (x != vec[i].size() - 1) tR = min(tR, vec[i][x + 1]);
for (int j = 1; j <= q; ++j) {
int lL = max(tL, ql[j] - 1), lR = min(tR, qr[j] + 1);
if (lL <= pos && pos <= lR) {
tmpc[j] += 1ll * (pos - lL) * (lR - pos);
}
}
}
for (int j = 1; j <= q; ++j) {
if (tmpc[j] > mxc[j]) {
mxc[j] = tmpc[j], mx[j] = i;
}
}
Vec tmp;
auto merge = [&](Vec a, Vec b) -> void {
tmp.resize(a.size() + b.size());
for (int i = 0, j = 0; i < a.size() || j < b.size(); ) {
if (i < a.size() && (j == b.size() || a[i] < b[j])) {
tmp[i + j] = a[i], ++i;
}
else {
tmp[i + j] = b[j], ++j;
}
}
};
merge(now, vec[i]), now = tmp;
}
now.push_back(0), now.push_back(n + 1);
sort(now.begin(), now.end());
LL ans = 0;
for (int i = 1; i <= q; ++i) {
LL gmax = 0;
for (auto it = now.begin(); next(it) != now.end(); ++it) {
int L = max(*it + 1, ql[i]), R = min(*next(it) - 1, qr[i]), len = max(R - L + 1, 0);
gmax += 1ll * len * (len + 1) / 2;
}
if (gmax > mxc[i]) mx[i] = query(ql[i], qr[i]);
ans ^= 1ll * mx[i] * i;
}
cout << ans << '\n';
}
return 0;
}

T1011 树上的 mex(HDU 7443)

首先可以二分 \(\text{mex} = k\),题意相当于求覆盖值域 \([0, k)\) 的路径数。

考虑同时覆盖一段值域不好做,转化成求不覆盖某一个值域的并集。我们枚举权值 \(v\),可以定位到树上点权为 \(v\) 的若干点,若这条路径不包含其中任何一个点则肯定不合法。

观察不合法的点对长什么样。我们发现对于一个 \(v\),将所有点权为 \(v\) 的点建虚树(可以在虚树中添加虚拟点作为根),对于一个虚树上的点 \(u\),设它的虚儿子集合为 \(Son_u\),在原树上的子树内点集为 \(Sub_u\),那么在点集 \(V = Sub_u - {u} - \bigcup\limits_{v \in Son_u} Sub_v\) 中的点两两配对都不合法。

观察到 \(V\) 在 \(\text{dfs}\) 序上可以划分为 \(O(Son_u)\) 个区间,两两配对形成 \(O((Son_u)^2)\) 个矩形,由于权值为 \(v\) 的点集中在一条路径上,我们有 \(|Son_u| \le 2\),所以对于一个 \(u\),矩形数量是 \(O(1)\) 的,总数就是 \(O(n)\) 的,扫描线 + 线段树算覆盖面积即可。

时间复杂度 \(O(n \log^2 n)\),常数较大。

Code (被卡常而无法通过)
#include <iostream>
#include <vector>
#include <algorithm>
#include <ctime>
#pragma GCC optimize("Ofast") using namespace std;
using Pr = pair<int, int>;
using LL = long long; const int N = 7e4 + 5; struct Rec {
int x, l, r, v;
}; int n;
int a[N], siz[N], rmp[N], dfn[N], now;
vector<int> e[N], vec[N];
vector<Rec> rv;
int lc[N], rc[N]; inline bool in_subtree (int u, int v) { return dfn[u] <= dfn[v] && dfn[v] < dfn[u] + siz[u]; } void build (int u, int r) {
siz[u] = 1;
rmp[dfn[u] = ++now] = u;
for (auto v : e[u]) {
if (v != r) {
build(v, u);
siz[u] += siz[v];
}
}
} void add_rec (int col) {
if (vec[col].empty()) {
rv.push_back((Rec){1, 1, n, 1});
rv.push_back((Rec){n + 1, 1, n, -1});
return;
}
int t1 = 0, t2 = 0;
for (auto u : vec[col]) {
lc[u] = rc[u] = 0;
if (t1 && in_subtree(u, t1)) {
lc[u] = t1, t1 = 0;
}
if (t2 && in_subtree(u, t2)) {
(!lc[u] ? lc[u] : rc[u]) = t2, t2 = 0;
}
if (dfn[lc[u]] > dfn[rc[u]]) swap(lc[u], rc[u]);
(!t1 ? t1 : t2) = u;
}
auto dfs = [&](auto &dfs, int u) -> void {
for (auto v : e[u]) if (dfn[v] > dfn[u]) {
vector<Pr> ve;
int vL = dfn[v], vR = dfn[v] + siz[v] - 1;
auto add = [&](int l, int r) -> void {
if (l > r) return;
ve.push_back({l, r});
};
auto cut = [&](int l, int r) -> void {
add(vL, l - 1), vL = r + 1;
};
if (lc[u] && in_subtree(v, lc[u])) cut(dfn[lc[u]], dfn[lc[u]] + siz[lc[u]] - 1);
if (rc[u] && in_subtree(v, rc[u])) cut(dfn[rc[u]], dfn[rc[u]] + siz[rc[u]] - 1);
add(vL, vR);
for (int i = 0; i < ve.size(); ++i) {
for (int j = 0; j < ve.size(); ++j) {
Rec tmpr1 = (Rec){ve[i].first, ve[j].first, ve[j].second, 1};
Rec tmpr2 = (Rec){ve[i].second + 1, ve[j].first, ve[j].second, -1};
rv.push_back(tmpr1), rv.push_back(tmpr2);
}
}
}
if (lc[u]) dfs(dfs, lc[u]);
if (rc[u]) dfs(dfs, rc[u]);
};
dfs(dfs, 0);
} namespace Seg {
#define lc (k << 1)
#define rc ((k << 1) | 1)
const int T = N * 4; int tag[T], cnt[T]; inline void pushup (int k, int L, int R) {
cnt[k] = (tag[k] ? R - L + 1 : (L != R ? cnt[lc] + cnt[rc] : 0));
} void add (int k, int l, int r, int v, int L = 1, int R = n) {
if (l <= L && r >= R) {
tag[k] += v;
}
else {
int mid = (L + R) >> 1;
if (l <= mid) {
add(lc, l, r, v, L, mid);
}
if (r > mid) {
add(rc, l, r, v, mid + 1, R);
}
}
pushup(k, L, R);
} inline void add_ (int l, int r, int v) { add(1, l, r, v); }
inline int count_ () { return cnt[1]; } #undef lc
#undef rc
} LL check (int goal) {
LL res = 1ll * n * n;
rv.clear();
for (int i = 0; i <= goal; ++i) {
add_rec(i);
}
auto cmp = [&](const Rec &a, const Rec &b) -> bool {
return a.x < b.x;
};
stable_sort(rv.begin(), rv.end(), cmp);
auto it = rv.begin();
for (int x = 1; x <= n + 1; ++x) {
while (it != rv.end() && it->x == x) {
Seg::add_(it->l, it->r, it->v);
++it;
}
res -= Seg::count_();
}
return res;
} int main () {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int T;
cin >> T;
while (T--) {
cin >> n;
fill(e + 1, e + n + 1, vector<int>());
fill(vec, vec + n + 1, vector<int>());
for (int i = 1; i <= n; ++i) {
cin >> a[i];
vec[a[i]].push_back(i);
}
for (int i = 1, u, v; i < n; ++i) {
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
now = 0, build(1, 0), siz[0] = n + 1, e[0].push_back(1);
for (int i = 0; i < n; ++i) {
vec[i].push_back(0);
auto cmp = [&](int i, int j) -> bool {
return siz[i] < siz[j];
};
stable_sort(vec[i].begin(), vec[i].end(), cmp);
}
int l = 0, r = n - 1;
LL rans = 0;
while (l <= r) {
int mid = (l + r) >> 1;
LL tmp = check(mid);
if (tmp) {
l = mid + 1;
rans = tmp;
}
else {
r = mid - 1;
}
}
cout << r + 1 << ' ' << rans << '\n';
}
return 0;
}

HDU-ACM 2024 Day1的更多相关文章

  1. hdu acm 1028 数字拆分Ignatius and the Princess III

    Ignatius and the Princess III Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K ...

  2. HDU ACM 题目分类

    模拟题, 枚举1002 1004 1013 1015 1017 1020 1022 1029 1031 1033 1034 1035 1036 1037 1039 1042 1047 1048 104 ...

  3. hdu acm 1166 敌兵布阵 (线段树)

    敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submi ...

  4. hdu acm 2082 找单词

    找单词 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...

  5. HDU ACM 1325 / POJ 1308 Is It A Tree?

    Is It A Tree? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tot ...

  6. HDU ACM 1134 Game of Connections / 1130 How Many Trees?(卡特兰数)

    [题目链接]http://acm.hdu.edu.cn/showproblem.php?pid=1134 [解题背景]这题不会做,自己推公式推了一段时间,将n=3和n=4的情况列出来了,只发现第n项与 ...

  7. HDU ACM Eight

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1043 解题背景: 看到八数码问题,没有任何的想法,偶然在翻看以前做的题的时候发现解决过类似的一道题,不 ...

  8. HDU ACM 1690 Bus System (SPFA)

    Bus System Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  9. HDU ACM 1224 Free DIY Tour (SPFA)

    Free DIY Tour Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tot ...

  10. HDU ACM 1869 六度分离(Floyd)

    六度分离 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

随机推荐

  1. 《Python数据可视化之matplotlib实践》 源码 第二篇 精进 第五章

    图 5.1 import matplotlib.pyplot as plt import numpy as np from matplotlib.ticker import AutoMinorLoca ...

  2. 在Vue3中如何为路由Query参数标注类型

    前言 最近发布了一款支持IOC容器的Vue3框架:Zova.与以往的OOP或者Class方案不同,Zova在界面交互层面仍然采用Setup语法,仅仅在业务层面引入IOC容器.IOC容器犹如一把钥匙,为 ...

  3. 洛谷P5250 【深基17.例5】木材仓库

    [深基17.例5]木材仓库 题目描述 博艾市有一个木材仓库,里面可以存储各种长度的木材,但是保证没有两个木材的长度是相同的.作为仓库负责人,你有时候会进货,有时候会出货,因此需要维护这个库存.有不超过 ...

  4. Gradle 项目打开自动下载Zip问题及相关配置

    原因 : 由于使用Eclipse开发,导入了SpringCloud 工程,SpringCloud 自从哪个版本忘了昂,选择了Gradle 作为工程管理工具,至于为啥,你去问问官方,我的了解是为了支持G ...

  5. 使用 Nuxt 3 的 defineRouteRules 进行页面级别的混合渲染

    title: 使用 Nuxt 3 的 defineRouteRules 进行页面级别的混合渲染 date: 2024/8/12 updated: 2024/8/12 author: cmdragon ...

  6. RabbitMq高级特性之死信队列 通俗易懂 超详细 【内含案例】

    RabbitMq高级特性之死信队列 又称 死信交换机 DLX 介绍 当消息成为 Dead message 后,会重新发送到另一个交换机,这个交换机就是 DLX(死信交换机) 消息成为死信的情况公有三种 ...

  7. 圣诞节快乐,教你用shell脚本实现一颗圣诞树。【小酷炫】

    前言 圣诞节到了! 一口君在这祝各位粉丝朋友圣诞节快乐! 祝各位考研的同学金榜题名! 祝找工作的朋友offer接到爆! 祝各位老板新年大发财源! 在此一口君特地用shell脚本画了一个圣诞树! 先来看 ...

  8. MySQL如何区分大小写

    MySQL CRUD 问题描述 mysql在Windows下是不区分大小写的,而Linux下区分大小写,Windows下将script文件导入MySQL后表名也会自动转化为小写,如果导入Linux服务 ...

  9. ubuntu 安装psycopg2包

    psycopg2 库是 python 用来操作 postgreSQL 数据库的第三方库. 执行:pip3 install psycopg2==2.8.4 有可能会报错: Collecting psyc ...

  10. Go 互斥锁 Mutex 源码分析 (一)

    0. 前言 锁作为并发编程中的关键一环,是应该要深入掌握的. 1. 锁 1.1 示例 实现锁很简单,示例如下: var global int func main() { var mu sync.Mut ...