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. 【Layui】02 图标 Icon

    官网下载地址: https://www.layui.com/ 学习参考: https://www.bilibili.com/video/BV1ct411n7SN [Layui的文件结构] 我们只需要这 ...

  2. 【转载】 softmax_cross_entropy_with_logits中“logits”是个什么意思?

    原文地址: https://zhuanlan.zhihu.com/p/51431626 -------------------------------------------------------- ...

  3. 【转载】 自然梯度法(Natural Gradient)

    原文地址: https://blog.csdn.net/philthinker/article/details/80615122 ----------------------------------- ...

  4. java集合专题之Collection接口

    1.背景 集合是java中非常重要的技术点,也是面试经常问到的技术点.... 2.集合体系 单列集合 双列集合,key value集合 如果出去面试,这体系应该可以背出来,才算合格 3.常用方法 代码 ...

  5. POI1999 Store-keeper 题解

    前言 题目链接:洛谷:SPOJ:hydro & bzoj. \(\Theta(nm)\) 的算法. 题意简述 在一个划分为 \(n \times m\) 个区域的二维仓库中,称有公共边的两个区 ...

  6. 用海豚调度器定时调度从Kafka到HDFS的kettle任务脚本

    在实际项目中,从Kafka到HDFS的数据是每天自动生成一个文件,按日期区分.而且Kafka在不断生产数据,因此看看kettle是不是需要时刻运行?能不能按照每日自动生成数据文件? 为了测试实际项目中 ...

  7. Java核心编程-第一卷:基础知识

    public static void main(String[] args) { BigInteger bigInteger1 = BigInteger.probablePrime(20, new R ...

  8. CH08_结构体

    CH08_结构体 基本概念 结构体属于用户自定义数据类型,允许用户存储不同的数据类型. 定义和使用 语法:struct 结构体名{ 结构体成员列表} 通过结构体创建变量的方式有三种: struct 结 ...

  9. 零基础学习人工智能—Python—Pytorch学习(五)

    前言 上文有一些文字打错了,已经进行了修正. 本文主要介绍训练模型和使用模型预测数据,本文使用了一些numpy与tensor的转换,忘记的可以第二课的基础一起看. 线性回归 结合numpy使用 首先使 ...

  10. maven依赖拉取小技巧

    依赖对应的dependency搜索库 https://mvnrepository.com/ 前往搜索  点击对应的依赖版本复制xml的代码  然后idea中刷新maven即可拉出来