做题记录3
CF2149E. Hidden Knowledge of the Ancients
思路
滑动窗口 + 双指针。
先不考虑长度的限制,求"恰好有 \(k\) 个不同的数"的区间。可以维护两个窗口,一个是以当前的位置为右端点,且第一个最多有 \(k\) 个不同元素 的区间;一个是以当前位置为右端点,且第一个 最多有 \(k - 1\) 个不同元素 的区间。那么以当前位置为右端点,且恰好有 \(k\) 个不同元素的子区间的数量就为 最多 \(k - 1\) 个 - 最多 \(k\) 个。其实就是它们的左端点相减的结果。再加上长度限制,要满足
所以对于当前的右端点,合法的左端点的数量就为 \(\min(最多 k - 1 个的左端点, c - l + 1) - \max(最多 k 个的右端点, c - r + 1)\) 。
由于 \(a\) 的范围很大,所以要先离散化。
代码
void solve(void) {
int n, k, l, r;
std::cin >> n >> k >> l >> r;
std::vector<int> a(n);
for(int i = 0; i < n; i++) std::cin >> a[i];
auto tmp = a;
std::sort(tmp.begin(), tmp.end());
tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end());
for(auto &i : a) {
i = std::lower_bound(tmp.begin(), tmp.end(), i) - tmp.begin();
}
int len = tmp.size();
i64 ans = 0;
std::vector<int> K(len), K1(len);
int disk = 0, disk1 = 0;
int lk = 0, lk1 = 0;
for(int i = 0; i < n; i++) {
K[a[i]]++;
if(K[a[i]] == 1) {
disk++;
}
while(disk > k) {
int x = a[lk++];
K[x]--;
if(K[x] == 0) {
disk--;
}
}
K1[a[i]]++;
if(K1[a[i]] == 1) {
disk1++;
}
while(disk1 > k - 1) {
int x = a[lk1++];
K1[x]--;
if(K1[x] == 0) {
disk1--;
}
}
int L = std::max(lk, i - r + 1);
int R = std::min(lk1 - 1, i - l + 1);
if(R >= L) ans += R - L + 1;
}
std::cout << ans << '\n';
}
CF2149F. Nezuko in the Clearing
思路
二分。
首先注意到,无论如何都是要走 \(d\) 个回合的,但是在这期间可能会休息若干次。因此可以表示为将 \(d\) 个回合分割为了 \(n\) 段,每段连续走 \(k_i\) 个回合,然后停下来休息一次,最后的答案就应该是:
在这期间,血量的总消耗是:
要保证消耗 \(\leq h - 1\) ,这样才能保证每一次触发的时候都有 \(\geq 1\) 的血量。
显然我们需要枚举分段的数量,如何 check 呢?
观察函数 \(f(k) = \frac{k(k + 1)}{2}\) ,我们想要让 \(\sum_{i = 1}^{n} f(k_i)\) 最小,注意到应该让每一段的长度都尽可能的接近,也就是说我们会分成 \(d \mod n\) 段 \(\lfloor \frac{d}{n} \rfloor + 1\) ,剩下的 $\lfloor \frac{d}{n} \rfloor $ ,然后就可以判断了。
但是如何证明这个结论呢?我不会
代码
void solve(void) {
i64 h, d;
std::cin >> h >> d;
if(h == 1) {
std::cout << d * 2 << '\n';
return;
}
i64 l = 1, r = d, len = -1;
auto check = [&](i64 x) -> bool {
i64 q = d / x;
i64 r = d % x;
i64 sum = r * (q + 1) * (q + 2) / 2;
i64 sum1 = (x - r) * q * (q + 1) / 2;
i64 total = sum + sum1 - (x - 1);
return total <= h - 1;
};
while(l <= r) {
i64 mid = (l + r) / 2;
if(check(mid)) {
r = mid - 1;
len = mid;
} else l = mid + 1;
}
//std::cout << len << '\n';
std::cout << (len - 1) + d << '\n';
}
牛客周赛 Round 111 D-小红的好数对
思路
注意到 \(11\) 的倍数有如下规律:奇数位之和减去偶数为之和是 \(11\) 的倍数。
两个数 \(A\) 与 \(B\) 拼接的结果是 \(A \times 10^{\lg B + 1} + B\) ,因为 \(10 \equiv -1 \pmod{11}\) , 要想让这个结果是 \(11\) 的倍数,有:
所以可以统计每个数字 \(\bmod 11\) 的结果。之后对于每一个数字 \(i\) ,当它作为第二个数时,如果是奇数位,那么能和它凑的数字的余数
然后就可以直接统计了。最后特判一下数字自己能和自己匹配的情况。
代码
void solve(void) {
int n; std::cin >> n;
std::vector<int> a(n + 1), r(n + 1), p(n + 1);
for(int i = 1; i <= n; i++) {
std::cin >> a[i];
r[i] = a[i] % 11;
int len = (int)std::to_string(a[i]).size();
p[i] = len & 1;
}
std::vector<int> cnt(11);
for(int i = 1; i <= n; i++) {
cnt[r[i]]++;
}
i64 ans = 0;
for(int i = 1; i <= n; i++) {
int x = 0;
if(p[i]) x = r[i];
else x = (11 - r[i]) % 11;
ans += cnt[x];
if(x == r[i]) ans--;
}
std::cout << ans << '\n';
}
CF766D. Mahmoud and a Dictionary
思路
扩展域并查集板子。
设单词 \(a + len\) 的翻译词为 \(a + len\) 。这样就可以做如下合并:
如果单词 \(a\) 和 \(b\) 是同义词,就合并 \((a, b)\) , \((a + len, b + len)\) ;
如果是反义词,就合并 \((a + len, b)\) 和 \((a, b + len)\) 。
每次合并操作前,先判断 \(a\) 和 \(b\) 之间是否已经存在了矛盾关系然后输出,接下来的 \(q\) 次询问直接判断所属集合就好了。
为了方便,可以做一个字符串和数字的映射。
代码
struct DSU {
std::vector<int> p, siz;
DSU(int n): p(n + 1), siz(n + 1, 1) {
std::iota(p.begin(), p.end(), 0);
}
int find(int x) {
if(x == p[x]) return p[x];
return p[x] = find(p[x]);
}
void unite(int a, int b) {
int pa = find(a), pb = find(b);
if(pa == pb) return;
if(siz[pa] < siz[pb]) std::swap(pa, pb);
p[pb] = pa;
siz[pa] += siz[pb];
}
bool same(int u, int v) {return find(u) == find(v);}
int size(int u) {return siz[find(u)];}
};
void solve(void) {
int n, m, q;
std::cin >> n >> m >> q;
std::vector<std::string> a(n);
for(int i = 0; i < n; i++) std::cin >> a[i];
auto tmp = a;
std::sort(tmp.begin(), tmp.end());
tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end());
int len = (int)tmp.size();
DSU dsu(len + len);
for(int i = 1; i <= m; i++) {
int op; std::string aa, bb;
std::cin >> op >> aa >> bb;
int x = std::lower_bound(tmp.begin(), tmp.end(), aa) - tmp.begin();
int y = std::lower_bound(tmp.begin(), tmp.end(), bb) - tmp.begin();
if(op == 1) {
if(dsu.same(x, y + len)) {
std::cout << "NO\n";
} else {
std::cout << "YES\n";
dsu.unite(x, y);
dsu.unite(x + len, y + len);
}
} else {
if(dsu.same(x, y)) {
std::cout << "NO\n";
} else {
std::cout << "YES\n";
dsu.unite(x + len, y);
dsu.unite(x, y + len);
}
}
}
for(int i = 1; i <= q; i++) {
std::string aa, bb;
std::cin >> aa >> bb;
int x = std::lower_bound(tmp.begin(), tmp.end(), aa) - tmp.begin();
int y = std::lower_bound(tmp.begin(), tmp.end(), bb) - tmp.begin();
if(dsu.same(x, y)) {
std::cout << "1\n";
} else if(dsu.same(x, y + len)) {
std::cout << "2\n";
} else std::cout << "3\n";
}
}

浙公网安备 33010602011771号