ARC181题解(A-D)
A - Sort Left and Right
答案为 0 即已经排序。
考虑答案为 1 的情况:一定是存在一个 \(p\),使得 \(\min_{i=1}^{p}a_i=p\) 且 \(a_p=p\),这时只要选择 \(p\) 即可。
考虑答案为 2 的情况:如果 \(a_1\neq n\operatorname{or}a_n\neq 1\),一定可以通过先操作某个数,把 \(1\) 或者 \(n\) 放到正确的位置,然后进行答案为 1 的操作即可。共操作 2 次。
答案一定不超过 3:如果 \(a_1= n\operatorname{and}a_n=1\),这时无法通过某种操作直接变成答案为 1 的情况。
所以只能先随便操作一下,就可以变成答案为 2 的情况。共操作 3 次。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
int n, a[N];
bool ok0()
{
for(int i = 1; i <= n; i ++)
if(a[i] != i) return 0;
return 1;
}
bool ok1()
{
int mx = 0;
for(int i = 1; i <= n; i ++)
{
mx = max(mx, a[i]);
if(i == a[i] && mx == a[i]) return 1;
}
return 0;
}
void solve()
{
cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i];
if(ok0()) return cout << 0 << "\n", void();
if(ok1()) return cout << 1 << "\n", void();
cout << 2 + (a[1] == n && a[n] == 1) << "\n";
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);
int t;cin >> t;while(t --) solve();
return 0;
}
B - Annoying String Problem
先猜一个结论:\(S\) 和 \(T\) 存在相同子串 \(P\),使得 \(S,T\) 都是一些 \(P\) 子串串在一起得到的,且 \(|P|=\gcd(|S|,|T|)\)。
我们猜测这是问题有解的充分必要条件。
于是可以设 \(S=aP,T=bP\)。可以通过解方程得到 \(a,b\)。
可以通过 \(x,y\) 列出方程:
\(\sum_{i=1}^{|x|}[x_i=0]a+\sum_{i=1}^{|x|}[x_i=1]b=\sum_{i=1}^{|y|}[y_i=0]a+\sum_{i=1}^{|y|}[y_i=1]b\)
然后移项解方程得出 \(a,b\) 关系。
令 \(k_1=\sum_{i=1}^{|x|}[x_i=0]-\sum_{i=1}^{|y|}[y_i=0],k_2=\sum_{i=1}^{|y|}[y_i=1]-\sum_{i=1}^{|x|}[x_i=1]\)
然后为了方便,去除最大公因数。
得出 \(k_1a=k_2b(\gcd(k_1,k_2)=1)\)。
于是有 \(\dfrac{a}{k_2}=\dfrac{b}{k_1}\)。因为 \(a\) 要是 \(k_2\) 倍数,于是只要判断是否存在 \(P'\) 使得 \(S=k_2P'\) 即可。
如果结论是必要的,那么存在 \(P'\) 使得 \(S=k_2P'\) 也是答案的必要条件。充分性显然。
对结论的证明:
考虑对 \(\Big||S|-|T|\Big|\) 进行归纳:
如果 \(|S|=|T|\),显然满足结论。
不妨设 \(|S|<|T|\)(\(|S|>|T|\) 同理)。
注意到如果 \(x\) 和 \(y\) 有公共前缀 \(pre\),那么可以同时去掉 \(pre\),答案不变。
所以现在 \(x\) 和 \(y\) 的第一个字符不同。
不妨设现在的 \(x_0=\texttt{0},y_0=\texttt{1}\)。
因为 \(f(S,T,x)=f(S,T,y)\) 并且 \(|S|<|T|\),所以可以把每个 \(1\) 拆成 \(01\),\(T=T'+S\),用 \(T'\) 替换 \(T\)。
这样变为了 \(\Big||S|-|T'|\Big|\) 的子问题,因为 \(\gcd(|S|,|T'|)=\gcd(|S|,|T|)\),根据归纳法,一定存在 \(P\),所以得证。
所以一定存在 \(P\),且 \(|P|=\gcd(|S|,|T|)\)。猜测的结论得证。
这证明了猜测结论的必要性,充分性直接构造即可证明。
中间要特判未知数系数为负数,为 0 的情况。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
bool ok(string s, int x)
{
if(s.size() % x) return 0;
int n = s.size(), d = n / x;
for(int i = 0; i < n; i += d)
{
for(int j = 0, k = i; j < d; j ++, k ++)
if(s[j] != s[k])
return 0;
}
return 1;
}
void solve()
{
string s; cin >> s;
string s1, s2;
cin >> s1 >> s2;
int x = 0, y = 0;
for(char c : s1) x += c == '0', y += c == '1';
for(char c : s2) x -= c == '0', y -= c == '1';
y = -y;
if(y == 0)
{
if(x == 0) return cout << "Yes" << "\n", void();
else return cout << "No\n", void();
}
if(x == 0) return cout << "Yes\n", void();
if(1ll * x * y < 0) return cout << "No\n", void();
if(x < 0) x = -x, y = -y;
int d = __gcd(x, y);
x /= d, y /= d;
if(ok(s, y)) cout << "Yes\n";
else cout << "No\n";
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);
int t;cin >> t;while(t --) solve();
return 0;
}
C - Row and Column Order
令 \(a,b\) 为满足 \(a_{p_i}=i,b_{q_i}=i\) 的排列。
考虑一种思路:
优先满足 \(p\),尽量捎带上 \(q\)。
考虑对于每个点 \((i,j)\),填入 \([a_i>b_j]\)。
然后我们发现,这样会满足 \(p\),并且反着满足了 \(q\)。
所以令 \(b'_{q_i}=n-i+1\)。
对于每个点 \((i,j)\),填入 \([a_i>b'_j]\) 即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 505;
int n, a[N], b[N];
int p[N], q[N];
int ans[N][N];
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);
cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i];
for(int i = 1; i <= n; i ++) cin >> b[i];
for(int i = 1; i <= n; i ++) p[a[i]] = i;
for(int i = 1; i <= n; i ++) q[b[i]] = n - i + 1;
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
ans[i][j] = p[i] > q[j];
for(int i = 1; i <= n; i ++, cout << "\n")
for(int j = 1; j <= n; j ++)
cout << ans[i][j];
return 0;
}
D - Prefix Bubble Sort
考虑枚举每个数,考虑什么时候会让答案的逆序对少 1。
观察性质,设 \(c_i=\sum_{j=1}^{i-1}[p_j>p_i]\)。
设 \(k_i\) 为最小的 \(d\) 使得 \(a_d\ge p_i\)。
我们发现,对于 \(p_i\),因为从第 \(k_i\) 次操作开始,前面比他大的数(如果存在),一定会有恰好一个越过它。
所以它会给从第 \(k_i\) 次开始的连续 \(c_i\) 次操作都贡献 -1 的逆序对数。
于是树状数组计算 \(c_i\),二分查找 \(k_i\),复杂度 \(O(n\log n)\)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
struct bit
{
int a[N];
int lbit(int x) {return x & -x;}
void upd(int x, int v)
{
for(int i = x; i < N; i += lbit(i)) a[i] += v;
}
int qry(int x)
{
if(!x) return 0;
int s = 0;
for(int i = x; i; i -= lbit(i)) s += a[i];
return s;
}
} t;
int n, a[N], m, b[N];
int d[N];
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);
cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i];
cin >> m;
for(int i = 1; i <= m; i ++) cin >> b[i];
ll sum = 0;
for(int i = 1; i <= n; i ++)
{
int p = i - 1 - t.qry(a[i]);
sum += p;
t.upd(a[i], 1);
int l = lower_bound(b + 1, b + m + 1, i) - b;
d[l] ++, d[min(m + 1, l + p)] --;
}
for(int i = 1; i <= m; i ++)
{
d[i] += d[i - 1];
sum -= d[i];
cout << sum << "\n";
}
return 0;
}
还可以枚举二分的答案,这样可以少一半常数。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
struct bit
{
int a[N];
int lbit(int x) {return x & -x;}
void upd(int x, int v)
{
for(int i = x; i < N; i += lbit(i)) a[i] += v;
}
int qry(int x)
{
if(!x) return 0;
int s = 0;
for(int i = x; i; i -= lbit(i)) s += a[i];
return s;
}
} t;
int n, a[N], m, b[N];
int d[N];
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);
cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i];
cin >> m;
for(int i = 1; i <= m; i ++) cin >> b[i];
ll sum = 0;
b[m + 1] = n;
for(int i = 1; i <= m + 1; i ++)
{
for(int j = b[i - 1] + 1; j <= b[i]; j ++)
{
int p = j - 1 - t.qry(a[j]);
sum += p;
t.upd(a[j], 1);
d[i] ++, d[min(m + 1, i + p)] --;
}
}
for(int i = 1; i <= m; i ++)
{
d[i] += d[i - 1];
sum -= d[i];
cout << sum << "\n";
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】