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】 08 子查询

    什么是子查询? 子查询是一种常用计算机语言SELECT-SQL语言中嵌套查询下层的程序模块. 当一个查询是另一个查询的条件时,称之为子查询. Oracle的子查询语法公式: SELECT select ...

  2. 中国特供版4090D已经开始发售

    由于美国政府的限制,NVIDIA公司等美国公司不允许向中国出口4090显卡,但是为了绕过美国政府的限制NVIDIA公司推出了中国特供版的4090D显卡. 4090d显卡和4090显卡区别大吗?可以说其 ...

  3. ubuntu系统下 vscode中如何指定conda环境

    参考: https://blog.csdn.net/mieleizhi0522/article/details/89336321 =================================== ...

  4. LeetCode279:完全平方数——动态规划算法——python语言

    无意间看到了这么一个题: LeetCode279:完全平方数,动态规划解法超过46%,作弊解法却超过97% ============================================= ...

  5. ubuntu系统conda下运行pytorch报错:ImportError: libopenblas.so.0: cannot open shared object file

    如题: ubuntu系统conda下运行pytorch报错:ImportError: libopenblas.so.0: cannot open shared object file 网上找了一些资料 ...

  6. Python 计算几月几号是某年的第几天

    闰年需要同时满足以下条件: 1.年份能被4整除: 2.年份若是 100 的整数倍的话需被400整除,否则是平年. #!/usr/bin/python # -*- coding: UTF-8 -*- # ...

  7. springboot2集成oauth2坑一(Possible CSRF detected - state parameter was required but no state could )

    码云地址:https://gitee.com/lpxs/lp-springcloud.git 有问题可以多沟通:136358344@qq.com. 刚开始用springboot1.5集成oauth2没 ...

  8. git忽略文件夹中除了指定的文件外的其他所有文件

    **/test/* !**/test/a.py

  9. 一文教你如何用C代码解析一段网络数据包?【含代码】

    本文的目的是通过随机截取的一段网络数据包,然后根据协议类型来解析出这段内存. 学习本文需要掌握的基础知识: 网络协议 C语言 Linux操作 抓包工具的使用 其中抓包工具的安装和使用见下文: < ...

  10. nginx编译安装-麒麟v10Arm64

    环境信息 操作系统: Kylin Linux Advanced Server V10 (Lance) 架构:Arm keepalived版本:1.25.5 编译 安装依赖包 yum install g ...