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. 【OracleDB】 07 分组查询 & 分组函数

    分组函数 分组函数作用于一组数据,并对一组数据返回一个值. Oracle中分组函数的种类: - 求平均值 AVG - 计数记录数 COUNT - 求最大值 MAX - 求最小值 MIN - 求和 SU ...

  2. 作为电脑屏幕的补光灯,到底是应该选Led灯还是荧光灯

    现在的台灯灯具市场基本被Led灯给霸占,这就无形之中要大家买台灯的时候只能选择Led等,我也是如此,手上有一款20年前上高中时候的"孩视宝"荧光灯的台灯,然后还有一款刚刚购入的Le ...

  3. (七)Redis 持久化 AOF、RDB

    Redis 一旦服务器宕机,内存中的数据将全部丢失,从后端数据库恢复这些数据,对数据库压力很大,且性能肯定比不上从 Redis 中读取,会拖慢应用程序.所以,对 Redis 来说,实现数据的 持久化 ...

  4. RabbitMq高级特性之TTL 存活时间/过期时间 通俗易懂 超详细 【内含案例】

    RabbitMq高级特性之TTL 存活时间/过期时间 介绍 RabbitMQ支持消息的过期时间, 在消息发送时可以进行指定 RabbitMQ支持队列的过期时间, 从消息入队列开始计算, 只要超过了队列 ...

  5. CH05_数组

    CH05_数组 概述 定义:一组相同类型的数据的集合. 描述: 1.数组中的每个元素都是相同的数据类型 2.数组是由连续的内存位置组成的. 3.数组索引是从0开始 一维数组 语法: 1.数据类型 数组 ...

  6. 如何将png转为svg

    如何将png转为svg如图所示. 工具/原料 Inkscape 方法/步骤 1 打开Inkscape,"文件-打开"如图. 2  打开你需要转化的png图片.如图所示. 3 打开你 ...

  7. String究竟能存储多少字符?

    能存储多少字符,通过以下步骤来看 首先String的length方法返回是int.所以理论上长度一定不会超过int的最大值. 编译器对字符串字面量长度的限制源自Java编译器(如javac)在处理常量 ...

  8. C# WinForm 解除资源文件的占用并删除

    1.删除未解除占用的资源时 2.调用Windows API函数 解除文件占用 [DllImport("kernel32.dll", SetLastError = true)] [r ...

  9. 使用JDBC查询数据库会一次性加载所有数据吗

    前几天有个小伙伴说他有个疑问:当我们发起一个查询的时候,数据库服务器是把所有结果集都准备好,然后一次性返回给应用程序服务吗(因为他们生产有个服务因为一个报表查询搞宕机了). 这样想的原因很简单,假设那 ...

  10. Python将表格文件中某些列的数据整体向上移动一行

      本文介绍基于Python语言,针对一个文件夹下大量的Excel表格文件,对其中的每一个文件加以操作--将其中指定的若干列的数据部分都向上移动一行,并将所有操作完毕的Excel表格文件中的数据加以合 ...