2025.7.31 Codeforces Round 1040 (Div. 1)

比赛链接

Solved: 4/9


A. Double Perspective

题意

给定 \(n\) 个二元组,对二元组集合 \(S\),定义 \(f(S)\) 为将二元组视为区间时这些区间的并集的长度,\(g(S)\) 为将二元组视为图中的边时环上的点数。选取一个子集使得 \(f(S) - g(S)\) 最大。

做法

\(g > 0\) 时大区间完全覆盖两个小区间,一定不优。所以 \(g\) 一定是 \(0\)

将区间按左端点从小到大排序,每次只选择能更新最右端的区间,这样同时保证了 \(f\) 最大和 \(g = 0\)

struct node
{
    int l, r, i;
    bool operator < (const node& p) const
    {
        return l < p.l || l == p.l && r < p.r;
    }
};
void solve(){
    int n; cin >> n;
    vector <node> a(n);
    for (int i=0; i<n; ++i) cin >> a[i].l >> a[i].r, a[i].i = i;
    sort(all(a));
    vector <int> ans;
    int R = 0;
    for (int i=0; i<n; ++i)
    {
        if (a[i].r > R) R = a[i].r, ans.push_back(a[i].i);
    }
    cout << ans.size() << '\n';
    for (int x: ans) cout << x+1 << ' ';
    cout << '\n';
}

B. Stay or Mirror

题意

给一个排列,可以将其中某些 \(p_i\) 替换成 \(2n-p_i\),求逆序对数的最小值。

做法

对于每个 \(p_i\),若不替换,则产生了 \(\sum_{j<i} [a_j > a_i]\) 的贡献;
若替换,则不换的贡献不产生,但额外产生了 \(\sum_{j>i} [a_j > a_i]\) 的贡献
(这里的额外贡献是指,后面的元素仍按 \(p_i\) 未替换来考虑,保证了没有后效性)。

我也不知道为啥这题要放 \(n^2\)

void solve(){
    cin >> n;
    for (int i=1; i<=n; ++i) cin >> a[i];
    int ans = 0;
    for (int i=1; i<=n; ++i)
    {
        int l = 0, r = 0;
        for (int j=1; j<i; ++j) l += a[j] > a[i];
        r = n-a[i]-l;
        ans += min(l, r);
    }
    cout << ans << '\n';
}

C. Interactive RBS

题意

交互题。有一个括号序列 \(S\)
每次询问,用户给出一个编号序列 \(i_1,i_2,\dots,i_k\)
交互库回答括号序列 \(S_{i_1}S_{i_2}\dots S_{i_k}\) 中正则括号序列子串数量。
\(n\leq 1000\),询问限制 \(k\leq 1000\)。在 500 / 200 / 100 次询问内确定 \(S\)

做法(只能压到 135 次左右)

首先 \(\log n\) 二分确定任意一个 ( 的位置 \(p\),然后每次取 8 个位置 \(p_0,\dots,p_7\),询问

\[p^2 p_0^1 p^3 p_1^2 p^1 p_2^4 p^9 p_3^8 p^{17} p_4^{16} p^{33} p_5^{32} p^{65} p_6^{64} p^{129}p_7^{128} \]

按答案的二进制位确定这 8 个位置是左还是右。

再多取一个就超过 1000 了。

这样询问次数是 \(\log n + \frac n8 \sim 135\)

int n;
int qry(vector <int> i)
{
    if (i.size() == 1) return 0;
    cout << "? " << i.size() << ' ';
    for (int x: i) cout << x << ' ';
    cout << endl;
    cout.flush();
    int res;
    cin >> res;
    return res;
}
void solve(){
    cin >> n;
    int l = 1, r = n;   // first ()
    int pos = -1;
    vector <int> s;
    for (int i=1; i<=n; ++i) s.push_back(i);
    if (qry(s) > 0)
    {
        while (l < r)
        {
            int mid = (l+r) / 2;
            vector <int> L, R;
            for (int i=l; i<=mid; ++i) L.push_back(i);
            for (int i=mid+1; i<=r; ++i) R.push_back(i);
            if (qry(L) > 0) r = mid;
            else
            {
                if (qry(R) > 0) l = mid+1;
                else
                {
                    pos = mid;
                    break;
                }
            }
        }
        if (!~pos) pos = l;
    }
    else pos = n;
    vector <char> ans(n+1);
    for (int i=1; i<=n; i+=8)
    {
        vector <int> c;
        for (int j=0; j<8; ++j)
        {
            if (i+j > n) break;
            for (int t=0; t<=1<<j; ++t) c.push_back(pos);
            for (int t=0; t<1<<j; ++t) c.push_back(i+j);
        }
        int val = qry(c);
        for (int j=0; j<min(8, n-i+1); ++j)
        {
            if (val >> j & 1) ans[i+j] = ')';
            else ans[i+j] = '(';
        }
    }
    cout << "! ";
    for (int i=1; i<=n; ++i) cout << ans[i];
    cout << endl;
    cout.flush();
}

然后 100 次的题解做法和这个类似,但只需要长度 \(O(\sum 2^{k/2})\) 的序列,每次可以确定 12 个位置。

posted @ 2025-08-02 10:57  EssnSlaryt  阅读(115)  评论(0)    收藏  举报