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\),询问
按答案的二进制位确定这 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 个位置。

浙公网安备 33010602011771号