首页 > 其他分享 >HDU-ACM 2024 Day1

HDU-ACM 2024 Day1

时间:2024-08-06 17:29:42浏览次数:4  
标签:HDU include int ACM 2024 vec rc now size

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,include,int,ACM,2024,vec,rc,now,size
From: https://www.cnblogs.com/hztmax0/p/18345251

相关文章

  • 力扣每日一题2024.8.5
    600.不含连续1的非负整数(困难)给定一个正整数n,请你统计在[0,n]范围的非负整数中,有多少个整数的二进制表示中不存在连续的1。示例1:输入:n=5输出:5解释:下面列出范围在[0,5]的非负整数与其对应的二进制表示:0:01:12:103:114:1005:101......
  • (连续四届EI检索|稳定ACM出版、EI检索|线上线下结合)2024年第五届医学人工智能国际学术
    第五届医学人工智能国际学术会议(ISAIMS2024)将于2024年8月13-17日于荷兰阿姆斯特丹自由大学召开,国内分会场将于2024年10月25-27日于中国武汉召开。会议自2020年至今已经成功举办四届,吸引了来自海内外相关领域学者600余名。本届会议将继续围绕人工智能在医学领域的最新研究成果,为......
  • 2024暑假集训测试18
    前言比赛链接。这次有大量外校人员参加,\(90\)来个人,T1胡了个结论上去结果大小样例都过了,造hack还没hack了,索性交了,但是有捆绑感觉会爆零,没想到结论是对的,直接A了;打完T1就罚坐了,三个小时就弄出来\(5\)分,当时都绝望了,想到了很多东西。因为感觉T1A不了,后面状态不......
  • 2024MX-MF-DAY1-text题解
    T1【题目描述】有\(n\)个人按编号从\(1\)到\(n\)坐成一圈,即第\(i\in[1,n]\)个人右边是\(i+1\),第\(n\)个人右边的人是\(1\)。初始,每个人手上有\(m\)个球。随后,\(n\)个人按编号从小到大的顺序依次执行如下操作:把自己手中的球分成数量相同且尽可能多的三份,......
  • 免费【2024】springboot 分类信息服务平台移动端的设计与实现
    博主介绍:✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌技术范围:SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数......
  • 免费【2024】springboot 微信小程序反诈科普平台的设计与实现
    博主介绍:✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌技术范围:SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数......
  • 免费【2024】springboot 房地产销售管理系统的设计与实现
    博主介绍:✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌技术范围:SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数......
  • 免费【2024】springboot 房屋租赁系统的设计与实现
    博主介绍:✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌技术范围:SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数......
  • 免费【2024】springboot 房地产销售管理系统的设计与实现
    博主介绍:✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌技术范围:SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数......
  • 免费【2024】springboot 分类信息服务平台移动端的设计与实现
    博主介绍:✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌技术范围:SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数......