P6578 魔法少女网站
P6578 魔法少女网站
给定一个长为 的序列,定义区间 的子区间为所有形如 的区间,满足 为整数,且 。
有 次操作:
1 x y
:将 位置的值修改为 。2 l r x
:查询区间 中有多少子区间的最大值小于或等于 。
,时限 ,空限 。
sol
第十分块。
难度评分:。
对于查询,考虑贡献,容易发现,设 ,那么我们现在的问题就是询问区间内 的所有为 的极长子区间的 之和。
再考虑怎么维护这个极长子区间。
由于 随时会变,相当于就是我们每次会修改一些点 或者 。
对于 这种情况,我们考虑把询问按 升序排序,然后我们发现这个满足的数会越来越多,然后分类讨论几种情况:
- 单独一个区间,直接加。
- 在一个区间的左边,直接加。
- 在一个区间的右边,直接加。
- 连接两个区间,修改区间信息后直接加。
但是对于 这种情况,即修改时可能会出现的情况,每一次都需要考虑,复杂度显然错误。
类似于回滚莫队的思想,将一个东西变成没有删除的。
容易发现 这种情况只会在这 个操作中出现,先将有修改的位置空出来,然后每一次询问时再将这些位置插入进去,记录一下改变的答案和指针个数,询问完了撤回即可,这样的话这些操作全部变成了将 换成 或 ,就可以维护了。
但是我们发现,这些有修改的位置最多有 个,每次询问都要修改,那么总时间复杂度是 的,直接爆炸。
考虑操作分块:每 次重建原序列,然后暂时储存的 次操作一起做。
这样这些有修改的位置最多有 个,每次询问都要修改,那么单个操作块的时间复杂度是 的,总时间复杂度是 的,可过。
查询时,散块记录,整块逐块扫过去即可。
所以总时间复杂度为 ,总空间复杂度为 。
总结:序列分块加操作分块再加大力卡常。
。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define re register
namespace Fread
{
const int SIZE = 1 << 23;
char buf[SIZE], *S, *T;
inline char getchar()
{
if (S == T)
{
T = (S = buf) + fread(buf, 1, SIZE, stdin);
if (S == T)
return '\n';
}
return *S++;
}
}
namespace Fwrite
{
const int SIZE = 1 << 23;
char buf[SIZE], *S = buf, *T = buf + SIZE;
inline void flush()
{
fwrite(buf, 1, S - buf, stdout);
S = buf;
}
inline void putchar(char c)
{
*S++ = c;
if (S == T)
flush();
}
struct NTR
{
~NTR()
{
flush();
}
} ztr;
}
#ifdef ONLINE_JUDGE
#define getchar Fread::getchar
#define putchar Fwrite::putchar
#endif
inline int read()
{
re int x = 0, f = 1;
re char c = getchar();
while(c < '0' || c > '9')
{
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9')
{
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
return x * f;
}
inline void write(re long long x)
{
if(x > 9)
write(x / 10);
putchar(x % 10 + 48);
}
inline int min(re const int &x, re const int &y)
{
return x < y ? x : y;
}
const int N = 3e5 + 1, S = 2e3 + 1;
struct Modify
{
int opt, pos, val;
} q1[S];
struct Query
{
int opt, l, r, x, id;
} q2[S];
int n, m, c1, c2, a[N];
long long calc[N];
bool seq[N];
int L[S], R[S], id[N], p[N], sum[S];
inline void clear()
{
memset(seq, 0, sizeof seq);
memset(p, 0, sizeof p);
memset(sum, 0, sizeof sum);
}
inline void init()
{
re int sqn = sqrt(n * 4 / 5 + 1);
for(re int i = 1, j, c = 1; i <= n; i = j + 1, ++c)
{
L[c] = i, R[c] = j = min(n, i + sqn);
for(re int t = L[c]; t <= R[c]; ++t)
id[t] = c;
}
}
int top;
struct Node
{
int id, ans, t1[4];
bool flg;
inline void clear()
{
t1[0] = t1[1] = t1[2] = t1[3] = flg = 0;
}
} sta[S], tmp;
inline void modify(re const int &pos, re bool flg)
{
p[pos] = pos, seq[pos] = 1, tmp.id = pos;
re const int lst = pos - 1, nxt = pos + 1, tmp_p = p[lst];
re const bool flg1 = (seq[lst] && L[id[pos]] != pos), flg2 = (seq[nxt] && R[id[pos]] != pos);
if(!flg1 && !flg2)
tmp.clear(), tmp.ans = 1;
else
{
tmp.flg = 1;
if(flg1 && flg2)
{
tmp.ans = (nxt - p[lst]) * (p[nxt] - lst);
tmp.t1[0] = p[lst], tmp.t1[1] = p[p[lst]], p[p[lst]] = p[nxt];
tmp.t1[2] = p[nxt], tmp.t1[3] = p[p[nxt]], p[p[nxt]] = tmp_p;
}
else
{
if(flg1)
{
tmp.ans = nxt - p[lst];
tmp.t1[0] = pos, tmp.t1[1] = p[pos], p[pos] = p[lst];
tmp.t1[2] = p[lst], tmp.t1[3] = p[p[lst]], p[p[lst]] = pos;
}
else
{
tmp.ans = p[nxt] - lst;
tmp.t1[0] = pos, tmp.t1[1] = p[pos], p[pos] = p[nxt];
tmp.t1[2] = p[nxt], tmp.t1[3] = p[p[nxt]], p[p[nxt]] = pos;
}
}
}
sum[id[pos]] += tmp.ans;
if(flg) sta[++top] = tmp;
}
inline void back()
{
while(top)
{
tmp = sta[top--], sum[id[tmp.id]] -= tmp.ans, seq[tmp.id] = 0;
if(tmp.flg)
{
p[tmp.t1[2]] = tmp.t1[3];
p[tmp.t1[0]] = tmp.t1[1];
}
}
}
inline long long query(const int &l, const int &r)
{
re long long ans = 0;
if(id[l] == id[r])
{
int cnt = 0;
for(re int i = l; i <= r; ++i)
if(seq[i])
cnt++;
else
ans += calc[cnt], cnt = 0;
return ans + calc[cnt];
}
re int c1 = 0, c2 = 0;
for(re int i = l; i <= R[id[l]]; ++i)
if(seq[i])
c1++;
else
ans += calc[c1], c1 = 0;
for(re int i = r; i >= L[id[r]]; --i)
if(seq[i])
c2++;
else
ans += calc[c2], c2 = 0;
re int tot = c1;
for(re int i = id[l] + 1; i <= id[r] - 1; ++i)
if(p[L[i]] == R[i])
tot += R[i] - L[i] + 1;
else
{
if(seq[L[i]])
{
tot += p[L[i]] - L[i] + 1, ans -= calc[p[L[i]] - L[i] + 1];
}
ans += calc[tot] + sum[i], tot = 0;
if(seq[R[i]])
{
tot += R[i] - p[R[i]] + 1, ans -= calc[R[i] - p[R[i]] + 1];
}
}
return ans + calc[tot + c2];
}
long long ans[S];
int cnt, head[N];
struct Edge
{
int to, nxt;
} G[N];
bool broke[N], use[N];
inline void add(const int &u, const int &v)
{
G[++cnt] = {v, head[u]};
head[u] = cnt;
}
inline bool cmp(const Query &x, const Query &y)
{
return x.x < y.x;
}
inline void solve()
{
clear();
for(re int i = 1; i <= c1; ++i)
broke[q1[i].pos] = 1;
for(re int i = 1; i <= n; ++i)
if(!broke[i])
add(a[i], i);
std::sort(q2 + 1, q2 + c2 + 1, cmp);
int pot = 1;
for(re int i = 1; i <= c2; ++i)
{
while(pot <= q2[i].x)
{
for(re int i = head[pot]; i; i = G[i].nxt)
modify(G[i].to, 0);
pot++;
}
for(re int j = c1; j >= 1; --j)
if(q1[j].opt < q2[i].opt && !use[q1[j].pos])
{
use[q1[j].pos] = 1;
if(q1[j].val <= q2[i].x)
modify(q1[j].pos, 1);
}
for(re int j = 1; j <= c1; ++j)
if(!use[q1[j].pos])
{
use[q1[j].pos] = 1;
if(a[q1[j].pos] <= q2[i].x)
modify(q1[j].pos, 1);
}
ans[q2[i].id] = query(q2[i].l, q2[i].r);
back();
for(re int j = 1; j <= c1; ++j)
use[q1[j].pos] = 0;
}
cnt = 0;
memset(head, 0, sizeof head);
for(re int i = 1; i <= c1; ++i)
broke[q1[i].pos] = 0;
}
signed main()
{
n = read(), m = read();
int sqm = sqrt(m * 11);
init();
for(re int i = 1; i <= n; ++i)
a[i] = read();
for(re int i = 1; i <= n; ++i)
calc[i] = 1ll * i * (i + 1) / 2;
for(re int i = 1, j; i <= m; i = j + 1)
{
j = min(m, i + sqm), c1 = c2 = 0;
for(re int t = i; t <= j; ++t)
if(read() == 1)
q1[++c1] = {t, read(), read()};
else
q2[++c2] = {t, read(), read(), read(), c2};
solve();
for(re int t = 1; t <= c2;++t)
write(ans[t]), putchar('\n');
for(re int t = 1; t <= c1; ++t)
a[q1[t].pos] = q1[t].val;
}
}
本文来自博客园,作者:蒟蒻orz,转载请注明原文链接:https://www.cnblogs.com/orzz/p/18122041
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话