qwq

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;
}

作者:adam01

出处:https://www.cnblogs.com/adam01/p/18342432

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   adam01  阅读(104)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up light_mode palette
选择主题