蒟蒻刚开始写题解,写得不好请见谅
算法交流群qq:891869943 欢迎进来一起玩~
A
找到最右边的 B 和最左边的 B 即可
void work(){
cin >> n;
string s; cin >> s;
cout << s.rfind('B') - s.find('B') + 1 << '\n';
}
B
题意:i 位置上的字母是第 a[ i ] + 1 次出现
我们直接让每个次数起始都为'a'
然后遍历数组,将目前次数的字符输出,同时变成下一个字母即可
void work(){
cin >> n;
vector<int> cnt(n, 'a');
for(int i = 0; i < n; i++){
int x; cin >> x;
cout << char(cnt[x]);
cnt[x]++;
}
cout << '\n';
}
C
对于1到k的每个数字,分以下四种情况
①:a中,b中都有,此时这个数字在哪选都可以
②:只有a有,我们只能从a中选这个数字,用一个cnt1记录这个数量
③:只有b有,我们只能从b中选这个数字,用一个cnt2记录这个数量
④:a, b都没有,此时无解
根据题意,我们知道cnt1 和 cnt2 <= k / 2
const int N = 2e5 + 10;
ll n, m, k;
int a[N], b[N];
void work(){
cin >> n >> m >> k;
//哈希表
vector<bool> has1(k + 1, false);
vector<bool> has2(k + 1, false);
for(int i = 1; i <= n; i++){
cin >> a[i];
if(a[i] <= k) has1[a[i]] = true;
}
for(int i = 1; i <= m; i++){
cin >> b[i];
if(b[i] <= k) has2[b[i]] = true;
}
int cnt1 = 0, cnt2 = 0;
for(int i = 1; i <= k; i++){
if(has1[i] && !has2[i]) cnt1++;
else if(!has1[i] && has2[i]) cnt2++;
else if(!has1[i] && !has2[i]){
cout << "NO" << '\n';
return;
}
}
if(cnt1 <= k / 2 && cnt2 <= k / 2) cout << "YES" << '\n';
else cout << "NO" << '\n';
}
D
记录两个数组s1,s2
s1[ i ]为下一个大于a[i]的位置,s2[ i ]为下一个小于a[ i ]的位置,显然可以用单调栈记录
对于每次查询[l, r],我们只要查询s1[ l ], 或者 s2[ l ] 是否小于等于 r 即可
const int N = 2e5 + 10;
ll n;
int a[N];
int q[N]; //数组模拟栈
void work(){
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
vector<int> s1(n + 3, n + 1);
vector<int> s2(n + 3, n + 1);
int t = 0; //栈指针
//单调栈
for(int i = n; i > 0; i--){
while(t && a[q[t]] >= a[i]) t--;
if(t) s1[i] = q[t];
q[++t] = i;
}
t = 0; //指针记得清零
for(int i = n; i > 0; i--){
while(t && a[q[t]] <= a[i]) t--;
if(t) s2[i] = q[t];
q[++t] = i;
}
int m; cin >> m;
while(m--){
int l, r; cin >> l >> r;
int R = min(s1[l], s2[l]);
if(l < R && R <= r) cout << l << " " << R << '\n';
else cout << "-1 -1" << '\n';
}
cout << '\n';
}
还有一位大佬群友的做法: 用set记录每个相邻数字不相同的下标,然后每次查询直接二分查找是否存在[ l , r ]范围的下标即可
const int N = 2e5 + 10;
ll n;
int a[N];
void work(){
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
set<int> s;
for(int i = 1; i < n; i++){
if(a[i] != a[i + 1]) s.insert(i);
}
int m; cin >> m;
while(m--){
int l, r; cin >> l >> r;
auto pos = s.lower_bound(l);
if(pos != s.end() && *pos < r) cout << *pos << " " << *pos + 1 << "\n";
else cout << "-1 -1" << '\n';
}
cout << '\n';
}
E
有一个比较容易发现的性质 abs( a[ i + k ] - a[ i ] ) <= 1;
那我们直接从数字1开始填充, 下标从1开始,每次加k,直到大于n后再从下标2继续开始,以此类推
以 n = 10 k = 4 为例 :
不过我们很快发现一个问题,前面4项和后面4项的和差了很多,不符合题意
因此我们不能只是递增填充,还需要递减填充来“中和”一下
我们这样:如果下标从奇数开始,我们就递增填充;如果是偶数,就递减填充
就变成这样
这样我们就可以组成一个合法的序列了
void work(){
cin >> n >> k;
int l = 1, r = n;
int p = 1;
while(p <= k){
int j = p;
if(p & 1){
while(j <= n){
a[j] = l;
l++;
j += k;
}
}else{
while(j <= n){
a[j] = r;
r--;
j += k;
}
}
p++;
}
for(int i = 1; i <= n; i++) cout << a[i] << " \n"[i == n];
}
F
有 tarjan 和 并查集 2种做法,这里介绍并查集做法
做法主要是判断当前便边是否成环
我们将边 按长度从大到小排序,然后逐步加边
Tips:为什么是从大到小?
举个例子:假如目前有5条边成环,长度分别是1 2 3 4 5,我们从大到小的话逐步加边就是 5 → 4 → 3 → 2 → 1,加 1 的时候发现成环了,此时 1 就是这个环中最小的边。
因此,我们从大到小加边,并且用并查集来判断是否成环
这样我们可以得到所有环中最小的那条边,以及最小边的两个端点u, v
这样我们直接以 u 为根节点, v 为父节点进行dfs,搜到 v 时直接用栈存储返回路径即可
const int N = 2e5 + 10;
ll n, m;
//并查集模板
struct DSU {
std::vector<int> f, sz;
DSU() {}
DSU(int n) { init(n);}
void init(int n) {
f.resize(n + 1);
std::iota(f.begin(), f.end(), 0);
sz.assign(n + 1, 1);
}
int find(int x) {
if(x == f[x]) return x;
return f[x] = find(f[x]);
}
bool same(int x, int y) {
return find(x) == find(y);
}
bool merge(int x, int y) {
x = find(x);
y = find(y);
if (x == y) return false;
sz[x] += sz[y];
f[y] = x;
return true;
}
int size(int x) {
return sz[find(x)];
}
};
struct node{
int u, v, w;
bool operator<(node b) const{
return w > b.w;
}
}e[N];
vector<int> adj[N];
int s[N];
int t = 0;
bool ok = false;
bool st[N];
//x为目标结点
void dfs(int u, int fa, int x){
//加个st数组标记路过的结点,防止进入死循环
st[u] = 1;
for(auto v : adj[u]){
if(v == fa) continue;
if(st[v]) continue;
if(v == x){
//找到目标结点直接返回
ok = true;
s[++t] = u;
return;
}
if(!ok) dfs(v, u, x);
if(ok){
s[++t] = u;
return;
}
}
};
void work(){
cin >> n >> m;
DSU dsu(n);
for(int i = 1; i <= m; i++){
int u, v, w;
cin >> u >> v >> w;
e[i] = node{u, v, w};
adj[u].push_back(v);
adj[v].push_back(u);
}
sort(e + 1, e + m + 1);
int res = 1e9, x1 = 0, x2 = 0;
for(int i = 1; i <= m; i++){
auto [u, v, w] = e[i];
if(dsu.same(u, v)){
res = w;
x1 = u;
x2 = v;
}else{
dsu.merge(u, v);
}
}
s[++t] = x2;
dfs(x1, x2, x2);
cout << res << " " << t << '\n';
for(int i = 1; i <= t; i++) cout << s[i] << " \n"[i == t];
ok = false;
t = 0;
for(int i = 1; i <= n; i++) {
adj[i].clear();
st[i] = false;
}
}