2022 ICPC 香港区域赛 FL
F. Sum of Numbers
高精度,\(dp\),暴搜。
要使得 \(n\) 位分成 \(k+1\) 段后相加结果数值最小,那么应该每段都尽可能的平分成 \(\frac n{k+1}\) 位,然后剩下的 \(n\%(k+1)\) 也平分,但这里可能会存在某段被多分两位,某段需要去掉一位,比如:
1
11 3
73992931311
1776
这里就是按照 \(2,2,3,4\) 的分法,\(4\) 就比 \(\frac{11}4\) 多两位,是个需要处理的细节。
减一位也有,这里提供一组数据:
1
100 6
1324842515955461999676716711641298225484496862171152246515638182113621698497576949417794359275794291
576285078539320
最多分成 \(7\) 段,\(\text{dfs}\) 暴搜每段,相邻段的长度差值在 \(-1,0,+1\),大概 \(3^7\) 次,中间应该有大量不合法的状态剪剪枝就过了,注意不要写成三进制枚举的写法,这样复杂度会多一个 \(k\),过不了。
点击查看代码
#pragma GCC optimize (3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
string operator+(string a,string b) {
if(a=="INF" || b=="INF") return "INF";
reverse(a.begin(),a.end());
reverse(b.begin(),b.end());
if(a.size()<b.size()) swap(a,b);
for(int i=0;i<b.size();i++) a[i]+=b[i]-'0';
for(int i=0;i<a.size();i++) {
if(a[i]>'9') {
a[i]-=10;
if(i+1==a.size()) a.push_back('1');
else a[i+1]++;
}
}
reverse(a.begin(),a.end());
return a;
}
bool operator<(string a,string b) {
if(a.size()!=b.size()) return a.size()<b.size();
for(int i=0;i<a.size();i++) {
if(a[i]!=b[i]) return a[i]<b[i ];
}
return false;
}
void solve() {
int n, k;
cin >> n >> k;
string s;
cin >> s;
vector<int> lt(k + 1);
vector<int> c(k + 1);
string ans = "";
function<void(int idx)> dfs = [&](int idx) {
if (idx > k) {
int sum = 0;
for (int i = 0; i <= k; ++i) {
lt[i] = c[i];
if (i)lt[i] += lt[i - 1];
sum += lt[i];
}
if ((n - sum) % (k + 1) == 0) {
int base = (n - sum) / (k + 1);
if (base>0) {
string res = "";
int len = 0;
int vs=1;
for (auto x: lt) {
if(x+base<=0) {
vs=0;
break;
}
}
if(vs) {
for (auto x: lt) {
if (x + base <= 0) {
vs = 0;
break;
}
res = res + s.substr(len, x + base);
len += base + x;
}
if (vs and ans.empty() or res < ans) {
swap(ans, res);
}
}
}
}
return;
}
for (int i = -1; i <= 1; ++i) {
c[idx] = i;
dfs(idx + 1);
}
};
dfs(1);
cout << ans << endl;
}
signed main() {
// freopen("in.txt", "r", stdin);
ios::sync_with_stdio(false);
cin.tie(0);
int t=1;
cin >> t;
while (t--) {
solve();
}
return 0;
}
考虑 \(dp[i][j]\) 表示前 \(j\) 位分成 \(i\) 段的最小值,那么转移的时候要保证每段在 \([\frac{n}{k+1}-1,\frac{n}{k+1}+2]\) 的范围内去取 \(dp[i][j]=\min(dp[i][j],dp[i-1][j-x])\),\(x\) 在即合法段的长度。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
struct BigInteger {
static const int BASE = 100000000;
static const int WIDTH = 8;
std::vector<int> s;
bool sign;
BigInteger(long long num = 0) {
*this = num;
}
BigInteger(const std::string& str) {
*this = str;
}
BigInteger operator = (long long num) {
s.clear();
sign = (num < 0);
if (num < 0) num = -num;
do {
s.push_back(num % BASE);
num /= BASE;
} while (num > 0);
return *this;
}
BigInteger operator = (const std::string& str) {
s.clear();
std::string numStr = str;
sign = false;
if (numStr[0] == '-') {
sign = true;
numStr = numStr.substr(1);
}
int x, len = (numStr.length() - 1) / WIDTH + 1;
for (int i = 0; i < len; i++) {
int end = numStr.length() - i * WIDTH;
int start = std::max(0, end - WIDTH);
sscanf(numStr.substr(start, end - start).c_str(), "%d", &x);
s.push_back(x);
}
return *this;
}
void normalize() {
while (s.size() > 1 && s.back() == 0) s.pop_back();
if (s.size() == 1 && s[0] == 0) sign = false;
}
bool operator < (const BigInteger& b) const {
if (sign != b.sign) return sign;
if (s.size() != b.s.size())
return sign ? s.size() > b.s.size() : s.size() < b.s.size();
for (int i = s.size() - 1; i >= 0; i--)
if (s[i] != b.s[i])
return sign ? s[i] > b.s[i] : s[i] < b.s[i];
return false;
}
bool operator > (const BigInteger& b) const {
return b < *this;
}
bool operator <= (const BigInteger& b) const {
return !(b < *this);
}
bool operator >= (const BigInteger& b) const {
return !(*this < b);
}
bool operator != (const BigInteger& b) const {
return b < *this || *this < b;
}
bool operator == (const BigInteger& b) const {
return !(b < *this) && !(*this < b);
}
BigInteger operator + (const BigInteger& b) const {
if (sign == b.sign) {
BigInteger c;
c.sign = sign;
c.s.clear();
for (int i = 0, g = 0; ; i++) {
if (g == 0 && i >= s.size() && i >= b.s.size()) break;
int x = g;
if (i < s.size()) x += s[i];
if (i < b.s.size()) x += b.s[i];
c.s.push_back(x % BASE);
g = x / BASE;
}
return c;
} else {
if (sign) {
BigInteger temp = *this;
temp.sign = false;
return b - temp;
} else {
BigInteger temp = b;
temp.sign = false;
return *this - temp;
}
}
}
BigInteger operator - (const BigInteger& b) const {
if (sign != b.sign) {
BigInteger temp = b;
temp.sign = !b.sign;
return *this + temp;
}
if (*this == b) return BigInteger(0);
BigInteger c;
if ((!sign && *this > b) || (sign && *this < b)) {
c = *this;
for (int i = 0, g = 0; i < c.s.size() || i < b.s.size(); i++) {
int x = c.s[i] - g;
if (i < b.s.size()) x -= b.s[i];
if (x < 0) {
g = 1;
x += BASE;
} else {
g = 0;
}
if (i < c.s.size()) c.s[i] = x;
else c.s.push_back(x);
}
c.normalize();
c.sign = sign;
} else {
c = b - *this;
c.sign = !sign;
}
return c;
}
BigInteger abs() const {
BigInteger result = *this;
result.sign = false;
return result;
}
friend std::ostream& operator << (std::ostream& out, const BigInteger& x) {
if (x.sign && !(x.s.size() == 1 && x.s[0] == 0)) out << '-';
out << x.s.back();
for (int i = x.s.size() - 2; i >= 0; i--) {
char buf[20];
sprintf(buf, "%08d", x.s[i]);
out << buf;
}
return out;
}
friend std::istream& operator >> (std::istream& in, BigInteger& x) {
std::string s;
if (!(in >> s)) return in;
x = s;
return in;
}
};
void solve() {
int n, k;
std::cin >> n >> k;
std::string s;
std::cin >> s;
k += 1;
int down = std::max(1, n / k - 1), up = std::min(n / k + 2, n - k + 1);
s = " " + s;
std::vector<BigInteger> dp(n + 1, -1);
dp[0] = 0;
for(int i = 1; i <= k; i += 1) {
std::vector<BigInteger> ndp(n + 1, -1);
for(int j = 1; j <= n; j += 1) {
if(down * i <= j && j <= up * i && down * (k - i) <= (n - j) && (n - j) <= up * (k - i)) {
for(int x = down; x <= up; x += 1) {
if(x > j || dp[j - x] == -1) continue;
std::string tmp = s.substr(j - x + 1, x);
if(ndp[j] == -1){
ndp[j] = dp[j - x] + BigInteger(tmp);
}else{
ndp[j] = std::min(ndp[j], dp[j - x] + BigInteger(tmp));
}
}
}
}
dp = std::move(ndp);
}
std::cout << dp[n] << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}
L. Permutation Compression
数据结构。
首先要让 \(a\) 删除数后得到 \(b\),显然要满足 \(b\) 是 \(a\) 的子集。
因为每次删除的都是最大值,考虑从大往小枚举可删除的数,找左右两边比当前数大的位置,假设 \(l\),\(r\) 代表左右两边比当前数大的位置,那么 \([l+1,r-1]\) 这个区间内都以 \(i\) 为最大值,那么只要存在 \(L_i\) 小于等于 \(r-l-1\) 就可以把 \(i\) 删掉;把 \(i\) 删掉后,为了防止 \(i\) 对后续的影响,需要标记一下,在后续有 \(j\) 找两边最大值时越过了 \(i\) 的位置,需要减掉它的影响,所以对 \(j\) 来说,删除 \(j\) 的区间长度为 \(r-l-1-cnt\),\(cnt\) 代表 \([l+1,r-1]\) 区间内有多少个比 \(j\) 大的数被删掉了,可以用线段树维护这个过程。
最后比较一下每个数可删除的区间长度是不是都能找到一个 \(L_i\) 对应即可,可以将两者排序后一一比较即可。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
template<class Info>
struct SegmentTree {
int n;
std::vector<Info> info;
SegmentTree() : n(0) {}
SegmentTree(int n_, Info v_ = Info()) {
init(n_, v_);
}
template<class T>
SegmentTree(std::vector<T> init_) {
init(init_);
}
void init(int n_, Info v_ = Info()) {
init(std::vector(n_, v_));
}
template<class T>
void init(std::vector<T> init_) {
n = init_.size();
info.assign(4 << std::__lg(n), Info());
std::function<void(int, int, int)> build = [&](int p, int l, int r) {
if (r - l == 1) {
info[p] = init_[l];
return;
}
int m = (l + r) / 2;
build(2 * p, l, m);
build(2 * p + 1, m, r);
pull(p);
};
build(1, 0, n);
}
void pull(int p) {
info[p] = info[2 * p] + info[2 * p + 1];
}
void modify(int p, int l, int r, int x, const Info &v) {
if (r - l == 1) {
info[p] = v;
return;
}
int m = (l + r) / 2;
if (x < m) {
modify(2 * p, l, m, x, v);
} else {
modify(2 * p + 1, m, r, x, v);
}
pull(p);
}
void modify(int p, const Info &v) {
modify(1, 0, n, p, v);
}
Info rangeQuery(int p, int l, int r, int x, int y) {
if (l >= y || r <= x) {
return Info();
}
if (l >= x && r <= y) {
return info[p];
}
int m = (l + r) / 2;
return rangeQuery(2 * p, l, m, x, y) + rangeQuery(2 * p + 1, m, r, x, y);
}
Info rangeQuery(int l, int r) {
return rangeQuery(1, 0, n, l, r);
}
template<class F>
int findFirst(int p, int l, int r, int x, int y, F &&pred) {
if (l >= y || r <= x) {
return -1;
}
if (l >= x && r <= y && !pred(info[p])) {
return -1;
}
if (r - l == 1) {
return l;
}
int m = (l + r) / 2;
int res = findFirst(2 * p, l, m, x, y, pred);
if (res == -1) {
res = findFirst(2 * p + 1, m, r, x, y, pred);
}
return res;
}
template<class F>
int findFirst(int l, int r, F &&pred) {
return findFirst(1, 0, n, l, r, pred);
}
template<class F>
int findLast(int p, int l, int r, int x, int y, F &&pred) {
if (l >= y || r <= x) {
return -1;
}
if (l >= x && r <= y && !pred(info[p])) {
return -1;
}
if (r - l == 1) {
return l;
}
int m = (l + r) / 2;
int res = findLast(2 * p + 1, m, r, x, y, pred);
if (res == -1) {
res = findLast(2 * p, l, m, x, y, pred);
}
return res;
}
template<class F>
int findLast(int l, int r, F &&pred) {
return findLast(1, 0, n, l, r, pred);
}
};
constexpr int inf = 1E9;
struct Info {
int max = -inf, cnt = 0;
};
Info operator+(const Info &l, const Info &r) {
return {std::max(l.max, r.max), l.cnt + r.cnt};
}
void solve() {
int n, m, k;
std::cin >> n >> m >> k;
SegmentTree<Info> seg(n);
std::vector<int> a(n), b(m), c(k), vis(n), pos(n);
for(int i = 0; i < n; i += 1) {
std::cin >> a[i];
a[i] -= 1;
pos[a[i]] = i;
seg.modify(i, {a[i], 0});
}
for(int i = 0; i < m; i += 1) {
std::cin >> b[i];
b[i] -= 1;
vis[b[i]] = 1;
}
for(int i = 0; i < k; i += 1) {
std::cin >> c[i];
}
int idx = 0;
for(int i = 0; i < n; i += 1) {
if(idx < m && a[i] == b[idx]) {
idx += 1;
}
}
if(idx != m) {
std::cout << "NO\n";
return;
}
std::vector<int> sz;
for(int i = n - 1; i >= 0; i -= 1) {
if(vis[i]) continue;
int l = -1,r = n;
auto x = seg.findLast(0, pos[i], [&](auto p) {
return p.max > i;
});
l = x;
x = seg.findFirst(pos[i] + 1, n, [&](auto p) {
return p.max > i;
});
if(x != -1) {
r = x;
}
l += 1, r -= 1;
sz.push_back(r - l + 1 - seg.rangeQuery(l, r + 1).cnt);
seg.modify(pos[i], {0, 1});
}
if(c.size() < sz.size()) {
std::cout << "NO\n";
return;
}
sort(c.begin(), c.end());
sort(sz.begin(), sz.end());
for(int i = 0; i < sz.size(); i += 1) {
if(c[i] > sz[i]) {
std::cout << "NO\n";
return;
}
}
std::cout << "YES\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}

浙公网安备 33010602011771号