Good Bye 2020
咕了好久的博客...回来诈个尸...
A. Bovine Dilemma
高是固定的,唯一的差别在于底。
由于 \(n\) 很小,所以两两枚举统计差值的不同个数即可。
Code
// Author : heyuhhh
// Created Time : 2020/12/31 09:15:16
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
set<int> s;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (a[i] != a[j]) {
s.insert(abs(a[i] - a[j]));
}
}
}
cout << sz(s) << '\n';
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
int T; cin >> T; while(T--)
run();
return 0;
}
B. Last minute enhancements
注意到 \(x_i\) 非递减。
那么从后往前贪心来做就行。
Code
// Author : heyuhhh
// Created Time : 2020/12/31 09:20:36
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
int n;
cin >> n;
vector<int> cnt(2 * n + 1);
for (int i = 0; i < n; i++) {
int x;
cin >> x;
--x;
++cnt[x];
}
int ans = 0;
for (int i = 2 * n - 1; i >= 0; i--) {
if (cnt[i]) {
if (!cnt[i + 1]) {
++cnt[i + 1], --cnt[i], ans += 2;
if (cnt[i] == 0) --ans;
} else {
++ans;
}
}
}
cout << ans << '\n';
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
int T; cin >> T; while(T--)
run();
return 0;
}
C. Canine poetry
题意:
给定一个字符串,现在一次操作可以修改任意一个位置的字符。
问最少修改多少次使得该串不存在长度大于1的回文串。
思路:
很有意思的一道题目。
注意到满足一个长度大于1的回文串,必然满足存在长度等于2或者3的回文串。所以我们只需要考虑消除所有长度等于2或者3的回文串。
考虑一个字符最多会影响左右各两个字符,而字符集大小为26,所以一定可以通过枚举找到一个与他们都不相等的字符。
所以从前往后逐个击破即可。
Code
// Author : heyuhhh
// Created Time : 2020/12/31 09:38:39
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
string s;
cin >> s;
int n = s.length();
int ans = 0;
for (int i = 1; i < n; i++) {
if (s[i - 1] == s[i] || (i > 1 && s[i] == s[i - 2])) {
++ans;
for (int j = 0; j < 26; j++) {
if (j != s[i - 1] - 'a'
&& (i - 2 < 0 || j != s[i - 2] - 'a')
&& (i + 1 >= n || j != s[i + 1] - 'a')
&& (i + 2 >= n || j != s[i + 2] - 'a')
) {
s[i] = char(j + 'a');
break;
}
}
}
}
cout << ans << '\n';
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
int T; cin >> T; while (T--)
run();
return 0;
}
D. 13th Labour of Heracles
题面较绕...不过稍微思考一下就可以发现,贪心来做就行。
代码应该比较好懂。
Code
// Author : heyuhhh
// Created Time : 2020/12/31 09:57:09
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
vector<int> d(n);
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
--u, --v;
++d[u], ++d[v];
}
priority_queue<pii> que;
ll ans = 0;
for (int i = 0; i < n; i++) {
que.push(MP(a[i], d[i] - 1));
ans += a[i];
}
cout << ans;
for (int i = 1; i < n - 1; i++) {
while (!que.empty()) {
pii cur = que.top(); que.pop();
if (cur.se == 0) continue;
ans += cur.fi;
--cur.se;
if (cur.se > 0) que.push(cur);
break;
}
cout << ' ' << ans;
}
cout << '\n';
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
int T; cin >> T; while(T--)
run();
return 0;
}
E. Apollo versus Pan
求和式子看似时间复杂度要爆炸,实际上我们可以利用二进制每位独立的性质,提前预处理来做。
大概就是先预处理,然后 \(O(n)\) 消掉后面的两个和式得到 \(\displaystyle f(j)=\sum_{k=1}^n(x_j|x_k)\)。
之后又通过预处理,\(O(n)\) 求得答案。
Code
// Author : heyuhhh
// Created Time : 2020/12/31 10:09:58
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5, MOD = 1e9 + 7;
void run() {
int n;
cin >> n;
vector<ll> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
vector<int> has(60);
for (int i = 0; i < n; i++) {
for (int j = 0; j < 60; j++) if (a[i] >> j & 1) {
++has[j];
}
}
vector<int> f(n);
for (int j = 0; j < n; j++) {
int res = 0;
for (int bit = 0; bit < 60; bit++) {
if (a[j] >> bit & 1) {
res = (res + (1ll << bit) % MOD * n % MOD) % MOD;
} else {
res = (res + (1ll << bit) % MOD * has[bit] % MOD) % MOD;
}
}
f[j] = res;
}
vector<int> g(60);
for (int i = 0; i < n; i++) {
for (int j = 0; j < 60; j++) {
if (a[i] >> j & 1) {
g[j] = (g[j] + f[i]) % MOD;
}
}
}
int ans = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < 60; j++) if (a[i] >> j & 1) {
ans = (ans + (1ll << j) % MOD * g[j] % MOD) % MOD;
}
}
cout << ans << '\n';
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
int T; cin >> T; while(T--)
run();
return 0;
}
F. Euclid's nightmare
题意:
给出 \(n\) 个 \(m\) 维01向量,每一个向量至多两个 \(1\)。
问多少种不同的情况能被这 \(n\) 个向量表出。
要求输出能表示出这些情况的一个最小向量集,这里的最小首先要求集合大小最小,其次向量按照输入顺序的字典序最小。
思路:
显然,我们可以大致将最终的结果分为两类:自由位、与其它位有联系的位。
接下来考虑怎么确定自由位:假设一个向量只有一个自由位,那么可以直接确定;假设一个向量有两个自由位,并且其中之一为自由位,那么这一位也变为自由位。
那我们考虑按顺序进行模拟,但可能出现这种情况:第 \(x\) 位为自由位,现有一个向量第 \(y,z\) 位为1,之后有一个向量第 \(x,y\) 位为1,那么 \(z\) 也“自动”变为了自由位。
所以我们可以考虑用并查集维护连通块来实现上述过程。
注意一个集合中只需要一个原本就是自由位的位即可,这样能满足集合大小最小。
确定与其它位有联系的位的话,按照上述思路自然而然地想到维护连通块。不过计算答案时有差异:假设连通块大小为 \(x\),那么对答案的贡献即为 \(2^{x-1}\)。因为我们只要把某两个缩成一个点,将他们看作一个自由位,最后就会有 \(x-1\) 个自由位。
Code
// Author : heyuhhh
// Created Time : 2020/12/31 10:38:31
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5, MOD = 1e9 + 7;
int qpow(ll a, ll b) {
ll res = 1;
while(b) {
if (b & 1) res = res * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return res;
}
void run() {
int n, m;
cin >> n >> m;
vector<int> ans;
int res = 1;
vector<int> f(m);
vector<int> siz(m, 1);
vector<int> has(m);
iota(all(f), 0);
auto find = [&] (int x) {
while (x != f[x]) {
int t = f[x];
f[x] = f[f[x]];
x = t;
}
return x;
};
auto Union = [&] (int x, int y) {
x = find(x), y = find(y);
if (x != y) {
if (has[x] > has[y]) {
swap(x, y);
}
if (has[x] == 0) {
f[x] = y;
siz[y] += siz[x];
return true;
}
}
return false;
};
for (int i = 0; i < n; i++) {
int k;
cin >> k;
if (k == 1) {
int x;
cin >> x;
--x;
int fx = find(x);
if (!has[fx]) {
has[fx] = 1;
ans.emplace_back(i);
}
} else {
int x, y;
cin >> x >> y;
--x, --y;
if (Union(x, y)) {
ans.emplace_back(i);
}
}
}
sort(all(ans));
for (int i = 0; i < m; i++) if (f[i] == i) {
if (has[i]) res = 1ll * res * qpow(2, siz[i]) % MOD;
else res = 1ll * res * qpow(2, siz[i] - 1) % MOD;
}
cout << res << ' ' << sz(ans) << '\n';
for (auto it : ans) {
cout << it + 1 << ' ';
}
cout << '\n';
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
G. Song of the Sirens
题意:
给定 \(s_0,t\) 两个字符串,定义 \(s_i=s_{i-1}t_{i-1}s_{i-1}\)。
之后会有 \(q\) 组询问,每组询问给出 \(id\ s\)。
现在回答 \(s_{id}\) 里面包含多少 \(s\)。
保证所有的 \(s\) 长度之和不超过 \(10^6\)。
思路:
这个题和济南的L题简直不能太像,济南就是因为两个bug没有出L呜呜呜(错失金牌)
一开始还想利用上 \(s_0\leq 100\) 这个条件,貌似最后根本用不上...
对于每一组询问,我们只需要找到最小的 \(k\),使得 \(|s_k|\geq |s|\)。这一段我们暴力计算答案。
之后因为是直接拼接,那么我们只需要利用 \(s_k\) 的头和尾。提前预处理所有的三元组 \((x,y,i)\),分别表示尾巴 \(x\),头部 \(y\),满足 \(x+y+1=|s|\) 并且对应的与 \(s\) 相等,并且 \(s[x] - 'a'= i\)。
我们记 \(f(i)\) 表示满足上述条件的三元组个数,\(0\leq i<26\)。这里可以hash或者kmp之类的计算。
接下来考虑计算答案。因为询问的 \(id\) 可能很大,虽然每一个位置的贡献我们很容易求,为2的若干次幂,但不可能一位一位去求。
思考后其实可以发现,我们只需要对每一类字符单独统计贡献即可,结合“每一类字符两两相邻的位置差不变”这一性质。所以对每一类字符最后乘以或者除以一个2的若干次幂就快速求得答案啦。
细节见代码吧,感觉类似预处理的思想很常见,一般的hash其实就类似于这样。
Code
// Author : heyuhhh
// Created Time : 2021/01/05 11:39:55
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 2e6 + 5, MOD = 1e9 + 7, INV = (MOD + 1) / 2;
inline int add(int x, int y) {
return x + y >= MOD ? x + y - MOD : x + y;
}
inline int dec(int x, int y) {
return x - y < 0 ? x - y + MOD : x - y;
}
inline int mul(int x, int y) {
return 1ll * x * y % MOD;
}
int pow2[N], inv2[N];
void init() {
pow2[0] = inv2[0] = 1;
for (int i = 1; i < N; i++) {
pow2[i] = mul(pow2[i - 1], 2);
inv2[i] = mul(inv2[i - 1], INV);
}
}
typedef unsigned long long ull;
template <unsigned mod, unsigned base>
struct rolling_hash {
unsigned int pg[N], val[N]; // val:1,2...n
rolling_hash() {
pg[0] = 1;
for(int i = 1; i < N; i++) pg[i] = 1ull * pg[i - 1] * base % mod;
val[0] = 0;
}
void build(const char *str) {
for(int i = 0; str[i]; i++) {
val[i + 1] = (str[i] + 1ull * val[i] * base) % mod;
}
}
unsigned int operator() (int l, int r) {
++r; //
return (val[r] - 1ull * val[l] * pg[r - l] % mod + mod) % mod;
}
};
struct dm_hasher {
//str:0,1...len-1
rolling_hash<997137961, 753> h1;
rolling_hash<1003911991, 467> h2;
void build(const char *str) {
h1.build(str); h2.build(str);
}
ull operator() (int l, int r) {
return ull(h1(l, r)) << 32 | h2(l, r);
}
}hasher, hasher2;
void run() {
int n, q;
cin >> n >> q;
string s0, t;
cin >> s0 >> t;
vector<vector<int>> pos(26);
for (int i = 0; i < n; i++) {
pos[t[i] - 'a'].emplace_back(i);
}
vector<int> c(n);
vector<int> last(26);
for (int i = 0; i < 26; i++) {
for (int j = sz(pos[i]) - 1; j >= 0; j--) {
if (j == sz(pos[i]) - 1) {
c[pos[i][j]] = 1;
last[i] = pos[i][j];
} else {
c[pos[i][j]] = add(c[pos[i][j + 1]], pow2[last[i] - pos[i][j]]);
}
}
}
while (q--) {
int id;
string s;
cin >> id >> s;
int len = s.length();
int k;
string res = s0;
int ans = 0;
for (int i = 0; i <= id; i++) {
if (res.length() >= len) {
k = i;
break;
}
if (i < id) res += t[i] + res;
}
if (res.length() < len) {
cout << 0 << '\n';
continue;
}
hasher.build(res.c_str());
hasher2.build(s.c_str());
ull val = hasher2(0, len - 1);
int resLen = res.length();
for (int i = 0; i + len - 1 < resLen; i++) {
int j = i + len - 1;
if (hasher(i, j) == val) {
++ans;
}
}
ans = mul(ans, pow2[id - k]);
vector<int> f(26);
for (int j = 1; j < len; j++) {
if (hasher(resLen - j, resLen - 1) == hasher2(0, j - 1)
&& hasher(0, len - j - 2) == hasher2(j + 1, len - 1)
) {
++f[s[j] - 'a'];
}
}
if (hasher(0, len - 2) == hasher2(1, len - 1)) {
++f[s[0] - 'a'];
}
// add t[k]....t[id - 1]
for (int i = 0; i < 26; i++) if (f[i]) {
int lAt = lower_bound(all(pos[i]), k) - pos[i].begin();
int rAt = lower_bound(all(pos[i]), id) - pos[i].begin() - 1;
int res = 0;
if (lAt == sz(pos[i])) continue;
if (rAt == sz(pos[i]) - 1) {
res = mul(f[i], mul(c[pos[i][lAt]], pow2[id - 1 - last[i]]));
} else {
int tmp = dec(c[pos[i][lAt]], c[pos[i][rAt + 1]]);
res = mul(f[i], mul(tmp, inv2[last[i] - id + 1]));
}
ans = add(ans, res);
}
cout << ans << '\n';
}
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
init();
run();
return 0;
}
重要的是自信,一旦有了自信,人就会赢得一切。