不补题目一时爽,一直不补一直爽(睡)
Codeforces Round 882 (Div. 2)
D. Professor Higashikata
题意:给定一个长为 字符串 ,给定 个区间表示字符串被截取的区间,有 次询问,每次询问先将 异或 ,然后问将截取的字符串 字典序最大至少需要交换多少次不同位置的字符。
题解:我们贪心地来考虑,首先对于一个 ,它在截取的字符串 中第一次出现位置是他对字典序产生贡献最大的位置,并且第一次位置出现越靠前贡献越大,由此我们对截取区间内的所有字符按照贡献大小离散排序, 我们定义一个容器 来存储。
想要字典序最大,于是我们就需要将 尽可能地往前放,而经过离散化,我们发现我们可以控制的最靠前的 的位数就是 ,其中 就是字符串 中 的个数。
举个例子,对于样例:
6 1 0
101010
1 4
我们得到 ,字符串 中 的个数为 ,即 ;我们发现我们想要字典序最大即 都在左边,我们能够通过操作换过来的 的位数由容器 的大小和实际字符串 的个数决定,从上例我们发现我们只能控制最左边 位。
接着往下,我们发现那么每次我们最小操作数不就是 可以控制的 的位数 - 位数内已经有的 的个数(注意位数和个数的区分)。
于是我们代码中分别维护一下 字符串 中 的个数 、位数内已经有的 的个数 cnt_ones。
具体代码如下:
#pragma GCC optimize(2)
#include <iostream>
#include <algorithm>
#include <bitset>
#include <chrono>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <map>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <vector>
#define int long long
#define pi acos(-1)
#define quick_cin() cin.tie(0),ios::sync_with_stdio(false)
#define endl "\n"
#define pb push_back
#define mkp make_pair
#define all(x) x.begin(), x.end()
#define ul (u << 1)
#define ur (u << 1 | 1)
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int N = 6000, M = 600, INF = 1e18;
const int mod = 998244353;
int T = 1;
int res = 0;
int ans = 0;
int n, m, k, q;
string s;
void solve()
{
cin >> n >> m >> q;
cin >> s;
vector<int> order;//离散化
set<int> S;
for (int i = 0; i < n; i ++ ) S.insert(i);
while (m -- )
{
int l, r;
cin >> l >> r;
l -- ;
auto it = S.lower_bound(l);
while (it != S.end() && *it < r)
{
order.pb(*it);
it = next(it);
S.erase(prev(it));
}
}
vector<int> pos(n, -1);
int cnt = count(all(s), '1');
for (int i = 0; i < order.size(); i ++ ) pos[order[i]] = i;
int cnt_ones = 0;//表示我们可以控制位数内已经存在的1的个数
for (int i = 0; i < order.size() && i < cnt; i ++ )
cnt_ones += (s[order[i]] == '1');
while (q -- )
{
int x;
cin >> x;
x -- ;
if (s[x] == '1')
{
cnt -- ;
//在cnt<order.size()的情况下,取min时cnt的改变会影响可控制的位数,所以需要判断
if (cnt < order.size())
cnt_ones -= (s[order[cnt]] == '1');
}
else
{
if (cnt < order.size())
cnt_ones += (s[order[cnt]] == '1');
cnt ++ ;
}
s[x] ^= 1;
if (pos[x] != -1 && pos[x] < cnt)
cnt_ones += (s[x] == '1' ? 1 : -1);
cout << min(cnt, (int)order.size()) - cnt_ones << endl;
}
}
signed main()
{
quick_cin();
// cin >> T;
while (T -- )
{
solve();
}
}
F. The Boss's Identity
题意:给定一个 个数的序列 ,此后 其中 。问第一个下标 满足 是多少。
题解:
当 的时候,我们模拟发现:
len=1: a1 a2 a3 a4
len=2: a1|a2 a2|a3 a3|a4
len=3: a4|a1|a2 a1|a2|a3 a2|a3|a4
len=4: a3|a4|a1|a2
不难发现第 行有 个数字,第 行有 个数字。所以总数字数量是 个。
每个数字都是一个子段,首先长度小的子段靠前,在长度相等的子段中,右端点小的子段靠前。
对于或/与/gcd子段有一个特点,就是序列中值不同的子段只有 种。所以我们可以找出以每个点为右端点的所有有效的子段,即长度最小并且右端点最小的子段,这样的子段只有 个,然后计算出每种取值至少需要多大的序号才能满足要求,查询时只需在序列中二分即可。
具体代码如下:
#pragma GCC optimize(2)
#include <iostream>
#include <algorithm>
#include <array>
#include <bitset>
#include <chrono>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <map>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <vector>
#define pi acos(-1)
#define quick_cin() cin.tie(0),ios::sync_with_stdio(false)
#define endl "\n"
#define pb push_back
#define mkp make_pair
#define all(x) x.begin(), x.end()
#define ul (u << 1)
#define ur (u << 1 | 1)
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int N = 6000, M = 600, INF = 1e18;
const int mod = 998244353;
int T = 1;
int res = 0;
int ans = 0;
int n, m, k, q;
void solve()
{
cin >> n >> q;
vector<int> a(n * 2);
for (int i = 0; i < n; i ++ )
cin >> a[i], a[i + n] = a[i];
vector<array<int, 3> > cand;
cand.reserve(n * 30);
vector<int> last(30, -1);
for (int i = 0; i < 2 * n; i ++ )
{
for (int j = 0; j < 30; j ++ )
if (a[i] >> j & 1)
last[j] = i;
auto b = last;
sort(all(b), greater<>());
int s = 0;
for (int j = 0; j < 30; j ++ )
{
if (b[j] == -1) break;
if (!j || b[j] != b[j - 1])
{
s |= a[b[j]];
int len = i - b[j] + 1;
int ed = (i < n ? i : i - n);
if (ed || len == 1)
cand.pb({s, len, ed});
}
}
}
sort(all(cand));
vector<array<int, 3> > v;
v.reserve(n * 30);
PII tmp = {n + 1, 0};
//从大到小遍历,用tmp存的位置就一直变小,与题意相应
for (int i = cand.size() - 1; i >= 0; i -- )
{
int j = i;
tmp = min(tmp, {cand[j][1], cand[j][2]});
while (j - 1 >= 0 && cand[j][0] == cand[j - 1][0])
{
j -- ;
tmp = min(tmp, {cand[j][1], cand[j][2]});
}
v.pb({cand[j][0], tmp.first, tmp.second});
i = j;
}
reverse(all(v));
auto get = [&](int x, int y)
{
if (x == 1) return y + 1ll;
if (x == n) return n + 1ll * (n - 2) * (n - 1) + 1;
return n + 1ll * (x - 2) * (n - 1) + y;
};
while (q -- )
{
int x;
cin >> x;
auto it = lower_bound(all(v), array<int, 3>{x + 1, 0, 0});
if (it == v.end()) cout << "-1\n";
else cout << get(it->at(1), it->at(2)) << endl;
}
}
signed main()
{
quick_cin();
cin >> T;
while (T -- )
{
solve();
}
}