Codeforces Round 1028 (Div. 1)
A. Gellyfish and Flaming Peony
题解
skip
完整代码
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 5e3 + 9;
int a[N], dis[N], n, T, g;
bool vis[N];
queue <int> q;
int main(){
scanf("%d", &T);
while(T--){
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
if(i == 1)
g = a[i];
else
g = __gcd(g, a[i]);
}
int cnt = 0;
for(int i = 1; i <= n; i++)
if(a[i] == g)
cnt++;
if(cnt > 0){
printf("%d\n", n - cnt);
continue;
}
memset(vis, 0, sizeof(vis));
memset(dis, 0, sizeof(dis));
while(!q.empty())
q.pop();
for(int i = 1; i <= n; i++){
if(!vis[a[i]]){
vis[a[i]] = true;
q.push(a[i]);
}
}
while(!q.empty()){
int u = q.front();
q.pop();
if(u == g)
break;
for(int j = 1; j <= n; j++){
int v = __gcd(u, a[j]);
if(!vis[v]){
dis[v] = dis[u] + 1;
vis[v] = true;
q.push(v);
}
}
}
printf("%d\n", dis[g] + n - 1);
}
return 0;
}
B. Gellyfish and Camellia Japonica
题解
我们如果将操作看成一棵树,将每次 \(c_{z_i} = \min(c_{x_i}, c_{y_i})\) 的操作看成将 \(z_i\) 新建一个点,并向 \(x_i\) 和 \(y_i\) 连边,那么我们会发现 \(b\) 序列中一个位置 \(i\) 会对应 \(a\) 序列中的一个集合 \(S_i\),那么原题就变成了找到一组构造方案,使得每个集合 \(S_i\) 中都至少有一个数等于 \(b_i\)。这是简单的,我们将 \(b\) 序列从小到大排序,每次将 \(S_i\) 中的所有数赋值成 \(b_i\),那么只要集合之间不存在包含关系,那么就一定可以构造出一组合法的方案。
我们换个方式理解这个构造,\(a\) 序列中一个位置 \(i\) 的值为所有包含 \(i\) 的集合 \(S_j\) 中 \(b_j\) 的最大值,因此我们倒着考虑,每次将 \(a_{x_i}\) 和 \(a_{y_i}\) 与 \(a_{z_i}\) 取 \(\max\)。此时如果 \(z_i \neq x_i\) 且 \(z_i \neq y_i\),那么 \(a_{z_i}\) 就没用了,直接赋值成 \(0\) 即可。得出 \(a\) 序列以后,再正着跑一遍,如果最终得到的是 \(b\) 序列,那么直接输出 \(a\) 序列即可,否则无解。时间复杂度 \(\mathcal O(n + q)\)。
完整代码
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 9;
int a[N], b[N], tmp[N], x[N], y[N], z[N], T, n, q;
int main(){
scanf("%d", &T);
while(T--){
scanf("%d%d", &n, &q);
for(int i = 1; i <= n; i++){
scanf("%d", &b[i]);
a[i] = b[i];
}
for(int i = 1; i <= q; i++)
scanf("%d%d%d", &x[i], &y[i], &z[i]);
for(int i = q; i >= 1; i--){
a[x[i]] = max(a[x[i]], a[z[i]]);
a[y[i]] = max(a[y[i]], a[z[i]]);
if(x[i] != z[i] && y[i] != z[i])
a[z[i]] = 0;
}
for(int i = 1; i <= n; i++)
tmp[i] = a[i];
for(int i = 1; i <= q; i++)
tmp[z[i]] = min(tmp[x[i]], tmp[y[i]]);
bool flag = true;
for(int i = 1; i <= n; i++){
if(tmp[i] != b[i]){
flag = false;
break;
}
}
if(!flag)
printf("-1\n");
else {
for(int i = 1; i <= n; i++)
printf("%d ", a[i]);
printf("\n");
}
}
return 0;
}
C. Gellyfish and Eternal Violet
题解
感觉这个泪水磨砺之剑就是大师之剑,闪耀相当于发光。
我们考虑最终要将怪物的生命值全部减到 \(1\),因此假设生命值的最小值为 \(mi\),那么我们需要 \(sum = \displaystyle\sum_{i = 1}^n h_i - mi\) 次普通攻击将怪物的生命值削平(因为群伤无法改变怪物生命值的相对大小)。因此我们可以设计出普通的 DP,设 \(dp_{i, mi, sum}\) 表示还有 \(i\) 次操作,目前生命值的最小值为 \(mi\),\(sum = \displaystyle\sum_{i = 1}^n h_i - mi\) 时,最终达成目标的概率。
首先,如果 \(mi = 1\) 且 \(sum = 0\),那么此时已经达成目标,DP 值为 \(1\),这是边界情况。其次,我们发现只要可以群伤(也就是 \(mi > 1\)),一定要群伤,因为群伤比普通攻击更能削减怪物的血量总和。接下来分情况讨论,如果此时 \(sum > 0\) 且 \(x > 1\),如果只能选择普通攻击,我们一定会去进行,因为我们最终的状态是 \(sum = 0\),因此 \(dp_{i, mi, sum} = p \times dp_{i - 1, mi - 1, sum} + (1 - p) \times dp_{i - 1, mi, sum - 1}\)。如果此时 \(sum = 0\),如果只能选择普通攻击,我们就需要斟酌一下,是将最小值减 \(1\) 并将 \(sum\) 重置成 \(n - 1\),还是等待下一次群伤,因此 \(dp_{i, mi, 0} = p \times dp_{i - 1, mi - 1, 0} + (1 - p) \times \max(dp_{i - 1, mi - 1, n - 1}, dp_{i - 1, mi, 0})\)。不过直接这样做的时间复杂度是 \(\mathcal O(n m V^2)\) 的,无法通过,因此需要考虑优化。
我们注意到如果某一时刻 \(sum = 0\) 了,那么之后 \(sum\) 就不会超过 \(n - 1\) 了,因此我们可以缩减状态数量。我们再设 \(f_{i, j}\) 表示目前进行了 \(i\) 次操作,当前 \(sum' = j\),且 \(sum'\) 中途没有变成 \(0\) 的概率。转移比较简单,就是 \(f_{i, j} = p \times f_{i - 1, j} + (1 - p) \times f_{i - 1, j + 1}\)。现在我们枚举将 \(sum\) 变成 \(0\) 的操作次数 \(i\),那么其中就会有 \(sum\) 次普通攻击,\(i - sum\) 次群伤,注意要这 \(i\) 次操作的最后一次不能为群伤,因此最终答案的表达式为 \(\displaystyle\sum_{i = sum}^m (1 - p) \times f_{i - 1, 1} \times dp_{m - i, \max(mi - (i - sum), 1), 0}\)(\(i\) 枚举到上界是因为可能提前到达目标状态,此时可以选择一直不操作)。此时时间复杂度就降低到了 \(\mathcal O(n m V)\)。
完整代码
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 25, M = 4e3 + 5, V = 4e2 + 5;
int a[N], n, m, v, T, sum;
double p, ans, dp[M][V][N], dp2[8005];
int main(){
scanf("%d", &T);
while(T--){
scanf("%d%d%lf", &n, &m, &p);
p /= 100, v = V, sum = ans = 0;
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
v = min(v, a[i]);
}
for(int i = 1; i <= n; i++)
sum += a[i] - v;
for(int i = 0; i <= m; i++)
dp[i][1][0] = 1;
for(int i = 1; i <= m; i++)
for(int x = 1; x <= v; x++){
if(x != 1)
dp[i][x][0] = p * dp[i - 1][x - 1][0] + (1 - p) * max(dp[i - 1][x - 1][n - 1], dp[i - 1][x][0]);
for(int s = 1; s < n; s++)
dp[i][x][s] = p * dp[i - 1][x - (x != 1)][s] + (1 - p) * dp[i - 1][x][s - 1];
}
if(sum < n){
printf("%.6lf\n", dp[m][v][sum]);
continue;
}
for(int i = 1; i <= sum + 1; i++)
dp2[i] = 0;
dp2[sum] = 1;
for(int i = 1; i <= m; i++){
if(i >= sum)
ans += (1 - p) * dp2[1] * dp[m - i][max(v - (i - sum), 1)][0];
for(int j = 1; j <= sum; j++)
dp2[j] = p * dp2[j] + (1 - p) * dp2[j + 1];
}
printf("%.6lf\n", ans);
}
return 0;
}
D. Gellyfish and Forget-Me-Not
题解
首先,我们可以将所有的 \(b_i\) 加入答案中,并将所有的 \(a_i \operatorname{and} b_i\),那么此时问题就变成了选不选 \(a_i\)。
由于在二进制下一个高位的 \(1\) 比所有低位的 \(1\) 的和都要大,因此我们考虑从高位往低位贪心,假设枚举到了第 \(i\) 位,最后一个第 \(i\) 位有值的数是 \(a_{pos}\),那么操作 \(pos\) 位置的那个人一定能决定这个 \(1\) 是否能保留到最终答案中。但是现在有一个问题,就是我们并不知道 \(pos\) 之前选择了哪些数,也就无法知道我们是否需要将 \(a_{pos}\) 加入答案。
考虑一种自适应的方法。假设我们现在希望将 \(a_{pos}\) 加入最终答案(可能目前答案第 \(i\) 位为 \(0\),且是 Flower 操作,或者目前答案第 \(i\) 位为 \(1\),且是 \(Gellyfish\) 操作),那么考虑到如果前面选择了奇数个第 \(i\) 位为 \(1\) 的数字,那么我们就不希望选择 \(a_{pos}\),否则就希望选择 \(a_{pos}\)。于是我们将 \(a_{pos}\) 加入答案,此时策略就变成了如果前面选择了奇数个第 \(i\) 位为 \(1\) 的数字,那么我们就希望选择 \(a_{pos}\),否则就不希望选择 \(a_{pos}\)。此时我们可以通过给 \(pos\) 前面每一个第 \(i\) 位为 \(1\) 的数异或上 \(a_{pos}\) 实现。时间复杂度 \(\mathcal O(n \log V)\)。
完整代码
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 9;
int a[N], b[N], T, n, ans;
char ch[N];
signed main(){
scanf("%lld", &T);
while(T--){
ans = 0;
scanf("%lld", &n);
for(int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
for(int i = 1; i <= n; i++){
scanf("%lld", &b[i]);
ans ^= b[i];
a[i] ^= b[i];
}
scanf("%s", ch + 1);
for(int i = 59; i >= 0; i--){
int pos = 0;
for(int j = n; j >= 1; j--){
if(a[j] >> i & 1){
if(!pos)
pos = j;
else
a[j] ^= a[pos];
}
}
if((!(ans >> i & 1) && ch[pos] == '1') || ((ans >> i & 1) && ch[pos] == '0'))
ans ^= a[pos];
a[pos] = 0;
}
printf("%lld\n", ans);
}
return 0;
}
E. Gellyfish and Mayflower
题解
做这道题目之前建议先去阅读图上算法学习笔记(一):图论基础知识、最短路相关、生成树相关中同余最短路的应用部分,然后你会发现这就是个板子。
我们考虑将序列上的做法搬到 DAG 上有什么问题,那就是我们可能无法经过全局性价比最优的节点。因此我们考虑钦定性价比最高的节点 \(u\),然后从 \(1\) 开始拓扑排序,每次只走性价比小于 \(u\) 的节点,看看所有 \(1\) 到 \(p\) 的路径中哪条路径更优。因此我们设 \(dp_{key, i, j}\) 表示以 \(key\) 号物品作为基准物品,目前在第 \(i\) 个点的同余最短路的数组的第 \(j\) 个位置的数。转移时,假设当前拓扑排序遍历到了 \(u\) 节点,\(u\) 有一个后继节点 \(v\),因为同余最短路数组一个值 \(f_j\) 表示 \(v \equiv i \bmod V\) 时最优解比全部取 \(V\) 优或劣多少,那么直接将同余最短路数组对应位置取 \(\max\) 即可。每当一个节点 \(u\) 的所有前驱节点都被遍历过了,就将 \(u\) 节点上的这个物品加入同余最短路中。
不过还有一个小问题,那就是我们即使钦定了某个节点 \(u\) 为性价比最高的节点,我们依然有可能走不到 \(u\),因此我们在 DP 状态中还需要记录一位 \(0 / 1\) 表示是否走到过 \(u\),每次 \(0\) 和 \(0\) 之间转移。如果走到了 \(u\),那么当前节点的 \(0\) 就可以转移到 \(1\)。
最后,如果 \(r \leq \max^2 \{w_i\}\),此时我们无法推出图上算法学习笔记(一):图论基础知识、最短路相关、生成树相关中的定理 2.4.3.1,也就无法使用同余最短路。不过此时我们直接跑暴力即可。时间复杂度 \(\mathcal O(n m \max \{w_i\} + m \max^2 \{w_i\} + nq)\)。
完整代码
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e2 + 9, M = 2e3 + 9, INF = 0x3f3f3f3f;
struct Edge{
int v, nex;
} e[M];
int head[N], ecnt;
void addEdge(int u, int v){
e[++ecnt] = Edge{v, head[u]};
head[u] = ecnt;
}
int w[N], v[N], tmp[N][N][N][2], deg[N], n, m, qq, maxn;//tmp[key][i][j][0 / 1] 表示以 key 号物品作为基准物品,在第 i 个点,且容量 % w[key] = j ,经没经过基准点时的最大收益
void add(int id, int i, int key){//将 i 号物品加入第 id 个点的背包,基准物品为 key
int cnt = __gcd(w[i], w[key]);
for(int j = 0; j < cnt; j++){
for(int k = j, c = 1; c <= 2; c += k == j){
int nex = (k + w[i]) % w[key];
if(tmp[key][id][k][0] != -INF)
tmp[key][id][nex][0] = max(tmp[key][id][nex][0], tmp[key][id][k][0] + v[i] - (k + w[i]) / w[key] * v[key]);
if(tmp[key][id][k][1] != -INF)
tmp[key][id][nex][1] = max(tmp[key][id][nex][1], tmp[key][id][k][1] + v[i] - (k + w[i]) / w[key] * v[key]);
k = nex;
}
}
}
queue <int> q;
void topu(int key){
while(!q.empty()){
int u = q.front();
q.pop();
if(u != key)
add(u, u, key);
else
for(int i = 0; i <= maxn; i++)
tmp[key][u][i][1] = tmp[key][u][i][0];
for(int i = head[u]; i; i = e[i].nex){
int to = e[i].v;
if(v[key] * w[to] < w[key] * v[to])
continue;
for(int j = 0; j <= maxn; j++){
tmp[key][to][j][0] = max(tmp[key][to][j][0], tmp[key][u][j][0]);
tmp[key][to][j][1] = max(tmp[key][to][j][1], tmp[key][u][j][1]);
}
deg[to]--;
if(deg[to] == 0)
q.push(to);
}
}
}
int dp[N][N * N];
void prework(){
q.push(1);
while(!q.empty()){
int u = q.front();
q.pop();
for(int i = 0; i <= maxn * maxn; i++)
if(i >= w[u])
dp[u][i] = max(dp[u][i], dp[u][i - w[u]] + v[u]);
for(int i = head[u]; i; i = e[i].nex){
int to = e[i].v;
for(int j = 0; j <= maxn * maxn; j++)
dp[to][j] = max(dp[to][j], dp[u][j]);
deg[to]--;
if(deg[to] == 0)
q.push(to);
}
}
}
signed main(){
scanf("%lld%lld", &n, &m);
for(int i = 1; i <= n; i++){
scanf("%lld%lld", &w[i], &v[i]);
maxn = max(maxn, w[i]);
}
for(int i = 1; i <= m; i++){
int u, v;
scanf("%lld%lld", &u, &v);
addEdge(u, v);
deg[v]++;
}
prework();
memset(tmp, -0x3f, sizeof(tmp));
for(int i = 1; i <= n; i++){
for(int k = 0; k < w[i]; k++)
tmp[i][1][k][0] = 0;
for(int j = 1; j <= n; j++)
deg[j] = 0;
for(int j = 1; j <= m; j++)
deg[e[j].v]++;
if(w[1] * v[i] > w[i] * v[1] || i == 1){
q.push(1);
topu(i);
}
}
scanf("%lld", &qq);
for(int i = 1; i <= qq; i++){
int p, r, ans = 0;
scanf("%lld%lld", &p, &r);
if(r <= maxn * maxn)
printf("%lld\n", dp[p][r]);
else {
for(int j = 1; j <= n; j++)
ans = max(ans, tmp[j][p][r % w[j]][1] + r / w[j] * v[j]);
printf("%lld\n", ans);
}
}
return 0;
}
F1. Gellyfish and Lycoris Radiata (Easy Version)
题解
这题非常好玩,也非常达芬,我做了 \(1\) 天半才过。
我们考虑到问题是在线,因此可以想到支持在线的操作分块(虽然我没想到),每 \(\sqrt q\) 个操作分成一个操作块。考虑到一次操作最多会使序列新增一个颜色段(颜色段内的所有集合都是一样的),因此一个操作块最多会使一个序列分成 \(\mathcal O(\sqrt q)\) 个颜色段,对于每个颜色段 \(i = [l_i, r_i]\),我们维护一个队列 \(q_i\) 表示段内的集合,\(rev_i\) 表示该段是否翻转,\([rl_i, rr_i]\) 表示这个颜色段对应原序列中的哪一段。
此时 \(1\) 操作和 \(2\) 操作比较好维护。我们找到所有需要插入或翻转的颜色段,如果 \(r\) 正好在一个颜色段 \(i\) 中间,我们就需要将该颜色段分裂。注意此时 \([rl_i, rr_i]\) 的分裂和 \(rev_i\) 有关。假设我们要将颜色段 \(i = [6, 10]\) 在 \(7\) 处分裂,\(i\) 对应的原序列中的区间为 \([1, 5]\)。如果 \(rev_i = 0\),那么直接将 \([1, 5]\) 分裂成 \([1, 2]\) 和 \([3, 5]\) 即可;否则就应该分裂成 \([4, 5]\) 和 \([1, 3]\)。队列 \(q_i\) 就直接复制,时间复杂度将在后文分析。
操作 \(3\) 不好维护,但是我们可以延迟删除。如果 \(x\) 小于等于当前操作数,就将 \(x\) 标记成删除。不过此时队列还需要维护一个 \(be_i\),表示最小的没被删除的数字。由于每次被插入集合的都是当前操作编号,而编号是单增的,因此 \(be_i\) 也是最靠前的没被删除的数字。
现在来处理查询操作。对于一个位置 \(p\),我们可以二分查找出当前操作块中包含 \(p\) 的颜色段 \(i\),然后遍历 \(i\) 的队列 \(q_i\),如果枚举到的数字没被删除,那么根据刚才的推导,这一定是这个队列中最小的元素,用它更新答案即可;否则就将 \(be_i\) 加 \(1\)。由于我们记录了 \([rl_i, rr_i]\),因此我们知道 \(p\) 在经过当前操作块之前所处的位置 \(p'\),也就是 \(p\) 在上一个操作块中所处的位置,因此我们不断往下查找即可。
现在来分析时间复杂度。先来考虑修改操作,我们发现一个操作块中最多会被插入 \(\mathcal O(\sqrt q)\) 个数字,而最多只会分裂 \(\mathcal O(\sqrt q)\) 次,每次都会将这 \(\mathcal O(\sqrt q)\) 个数字复制一遍,因此一个操作块内操作的时间复杂度是 \(\mathcal O(q)\),这部分的总时间复杂度就是 \(\mathcal O(q \sqrt q)\)。而查询时,由于我们记录了 \(be_i\),而 \(be_i\) 是单调不降的,因此每个颜色段只会被扫一遍,而颜色段的数量长度和是 \(\mathcal O(n \sqrt q)\) 的。加上二分查找的时间复杂度,这一部分的时间复杂度就是 \(\mathcal O(n \sqrt q \log \sqrt q)\),总时间复杂度就是 \(\mathcal O(q \sqrt q + n \sqrt q \log \sqrt q)\)。
完整代码
点击查看代码
#include <bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0; bool f(0); char ch = getchar();
while(!isdigit(ch)) f = (ch == '-'), ch = getchar();
while(isdigit(ch)) x = (x<<3) + (x<<1) + (ch^48), ch = getchar();
return f ? -x : x;
}
inline void write(int x){
if(x < 0) x = -x, putchar('-');
static short Stack[64], top(0);
do Stack[++ top] = x % 10, x /= 10; while(x);
while(top) putchar(Stack[top--] | 48);
}
const int N = 2e5 + 9, B = 319, INF = 1e9 + 9;
struct Segment{
int l, r, rl, rr;
inline bool operator < (const Segment &a) const{
return l < a.l;
}
} t[N];
struct Node{
int pre, nex;
} e[N];
int head[N];
inline void add(int id, int x){//在 id 后面插入 x
e[x].nex = e[id].nex;
e[x].pre = id;
e[id].nex = x;
}
inline void del(int id){
e[e[id].nex].pre = e[id].pre;
e[e[id].pre].nex = e[id].nex;
}
int era[N], rev[N], q[N][B], be[N], ed[N], n, qq, tot = 1, cnt, num = 1, lastans;
void split(int i, int b){
tot++;
for(int j = be[i]; j <= ed[i]; j++)
q[tot][++ed[tot]] = q[i][j];
rev[tot] = rev[i];
int tmp1, tmp2, tmp3, tmp4;
if(rev[i]){
tmp1 = t[i].rr - (b - t[i].l + 1) + 1;
tmp2 = t[i].rr;
tmp3 = t[i].rl;
tmp4 = t[i].rr - (b - t[i].l + 1);
} else {
tmp1 = t[i].rl;
tmp2 = t[i].rl + (b - t[i].l + 1) - 1;
tmp3 = t[i].rl + (b - t[i].l + 1);
tmp4 = t[i].rr;
}
t[tot] = Segment{b + 1, t[i].r, tmp3, tmp4};
t[i].r = b;
t[i].rl = tmp1;
t[i].rr = tmp2;
add(i, tot);
}
int st[N][B], sum[B];
vector <int> vec;
int main(){
for(int i = 1; i < N; i++)
be[i] = 1;
n = read();
qq = read();
int k = sqrt(qq), qqq = qq;
t[tot]= Segment{1, n, 1, n};
e[tot].pre = 0;
e[0].pre = -1;
e[0].nex = tot;
e[tot].nex = 2 * n;
e[2 * n].pre = tot;
e[2 * n].nex = -1;
head[1] = tot;
while(qqq--){
cnt++;
int a, b, c;
a = read();
b = read();
c = read();
if(a == 1 || a == 2)
b = (b + lastans - 1) % n + 1;
else
b = (b + lastans - 1) % qq + 1;
c = (c + lastans - 1) % n + 1;
if(a == 1){
int he = 0;
for(int i = head[num]; i != 2 * n; i = e[i].nex){
he += t[i].r - t[i].l + 1;
if(he == b){
q[i][++ed[i]] = cnt;
break;
}
if(he > b){
split(i, b);
q[i][++ed[i]] = cnt;
break;
}
q[i][++ed[i]] = cnt;
}
} else if(a == 2){
int he = 0;
vec.clear();
for(int i = head[num]; i != 2 * n; i = e[i].nex){
he += t[i].r - t[i].l + 1;
if(he == b){
vec.push_back(i);
del(i);
break;
}
if(he > b){
split(i, b);
vec.push_back(i);
del(i);
break;
}
vec.push_back(i);
del(i);
}
for(int i = 0; i <= (int)vec.size() - 1; i++){
int L = t[vec[i]].l, R = t[vec[i]].r;
t[vec[i]].l = b - R + 1;
t[vec[i]].r = b - L + 1;
add(0, vec[i]);
rev[vec[i]] ^= 1;
}
head[num] = vec[vec.size() - 1];
} else if(a == 3){
if(b <= cnt)
era[b] = true;
}
sum[num] = 0;
for(int i = head[num]; i != 2 * n; i = e[i].nex)
st[num][++sum[num]] = i;
int ans = INF;
for(int i = num; i >= 1; i--){
int L = 1, R = sum[i], res = 0, j;
while(L <= R){
int mid = (L + R) >> 1;
if(t[st[i][mid]].l <= c)
L = mid + 1, res = mid;
else
R = mid - 1;
}
j = st[i][res];
for(int l = be[j]; l <= ed[j]; l++){
if(!era[q[j][l]]){
ans = min(ans, q[j][l]);
break;
} else
be[j]++;
}
c = t[j].rl + (c - t[j].l + 1) - 1;
if(rev[j])
c = t[j].rl + t[j].rr - c;
}
if(ans == INF)
ans = 0;
write(lastans = ans);
putchar('\n');
if(cnt % k == 0){
sum[num] = 0;
for(int i = head[num]; i != 2 * n; i = e[i].nex)
st[num][++sum[num]] = i;
num++, tot++;
t[tot] = Segment{1, n, 1, n};
e[tot].pre = 0;
e[0].pre = -1;
e[0].nex = tot;
e[tot].nex = 2 * n;
e[2 * n].pre = tot;
e[2 * n].nex = -1;
head[num] = tot;
}
}
return 0;
}
本文来自博客园,作者:Orange_new,转载请注明原文链接:https://www.cnblogs.com/JPGOJCZX/p/19155017

浙公网安备 33010602011771号