题解:P12401 [COI 2025] 玻利维亚 / Bolivija
Q = 0 时的解法(O(n + \mathrm{mx}) 暴力)
首先,如果一个
设
(对于样例
那么,要统计的答案即为“
(在样例
于是我们可以在本题中解决
代码:
#include <bits/stdc++.h>
#define _eggy_ using
#define _party_ namespace
_eggy_ _party_ std;
#define sjm 654210
int n,m,i;
int a[200100], c[sjm], fail[sjm]; // fail[i]表示第i层有多少个柱子不对称
long long ans,len0;
#define T(x) (((x)*((x)+1))>>1)
int main()
{
scanf("%d%d",&n,&m);
for (i = 1; i <= n; i++) scanf("%d",a+i);
for (i = 1; (i << 1) <= n; i++)
{
c[min(a[i],a[n-i+1])+1]++;
c[max(a[i],a[n-i+1])+1]--;
}for (i = 1; i <= a[(n+1)>>1]; i++)
{
fail[i] = fail[i-1] + c[i];
if (!fail[i]) len0++;
else
{
ans += T(len0);
len0 = 0;
}
}ans += T(len0);
printf("%lld",ans);
return 0;
}
正解(O(n + Q \log \mathrm{mx}) )
题面翻译
当
于是我们完成了本题的中译中:
给出一个非负整数数列
fail ,要求支持以下2 种操作:
- 给出
l,r,v ,将区间[l,r] 内每个数增加v 。v \in \{1,-1\} 。- 查询整个数列内有多少个区间内部只有
0 。
现在可以开始做题了。
正解思路
操作
容易想到,把左右儿子的合法区间数(设为 ans)相加。但还有一些区间跨越了中点。如果左儿子的最右端和右儿子的最左端都是 llen),和从右端点往左最长连续 rlen)。
但是这样会产生一个严重的问题:如果一个区间内的数没有 ans 就不得不递归计算它的所有儿子,这样的时间复杂度将会是
再想想,对于每一个 map 只维护区间内出现的数,也要在每次合并时遍历整个 map。
经过三节课的思考后……
给出一个非负整数数列……
我们只需要维护当区间的最小值被减成
于是我们还要在线段树上维护区间最小值(设为 minn)。为了判断区间合并时的交界处和区间最小值是否一样,我们还需要维护区间最左边的数(设为 lis)和最右边的数(设为 ris)。相应地,llen 和 rlen 的定义分别被改为从左到右/从右到左最长连续 lis/ris 的个数。
AC code
#include <bits/stdc++.h>
#define _eggy_ using
#define _party_ namespace
_eggy_ _party_ std;
int n,m,i,k;
#define sjm 654210 //(萨哈马火山的高度为 654200cm)
int a[200100], c[sjm], fail[sjm]; // fail[i]表示第i层有多少个位置不对称
long long ans,len0;
#define T(x) (((x)*((x)+1ll))>>1) // x 可以为int
#define cint const int&
template<unsigned char ce> //关于编译期确定线段树结构的方法 :查询 https://www.luogu.com.cn/article/u3r9m9nj
struct dzpd
{
struct dzpd <ce-1> ls,rs;
int l,r,lis,llen,ris,rlen,added,minn;
long long ans;
void up()
{
lis = ls.lis, ris = rs.ris;
llen = ls.llen, rlen = rs.rlen;
if (ls.lis == rs.lis && ls.llen == ls.r - ls.l + 1)
llen += rs.llen;
if (rs.ris == ls.ris && rs.rlen == rs.r - rs.l + 1)
rlen += ls.rlen;
if (ls.minn < rs.minn)
{
minn = ls.minn;
ans = ls.ans;
}
else if(ls.minn > rs.minn)
{
minn = rs.minn;
ans = rs.ans;
}else
{
minn = ls.minn;
ans = ls.ans + rs.ans;
if (ls.ris == minn && rs.lis == minn)
{
ans -= T(ls.rlen);
ans -= T(rs.llen);
ans += T(ls.rlen + rs.llen);
}
}
}
void down()
{
if (!added) return;
ls.lis += added, ls.ris += added,
ls.minn += added, ls.added += added;
rs.lis += added, rs.ris += added,
rs.minn += added, rs.added += added;
added = 0;
}
void bd(cint bdl, cint bdr) //建树
{
l = bdl, r = bdr, added = 0;
if (bdl == bdr)
{
lis = ris = minn = fail[bdl];
llen = rlen = ans = 1;
return;
}
ls.bd(bdl,(bdl+bdr)>>1);
rs.bd(((bdl+bdr)>>1)+1,bdr); up();
}
void add(cint t1, cint t2, cint v) //v = ±1
{
if (t1 <= l && r <= t2)
{
lis += v, ris += v, minn += v, added += v;
return;
}down();
if (t1 <= ls.r) ls.add(t1,t2,v);
if (t2 >= rs.l) rs.add(t1,t2,v); up();
}
};
template<>
struct dzpd<0>
{
int l,r,lis,llen,ris,rlen,added,minn;
long long ans;
void bd(cint bdl, cint bdr)
{
l = bdl, r = bdr, added = 0;
lis = ris = minn = fail[bdl];
llen = rlen = ans = 1;
}
void add(cint t1, cint t2, cint v)
{lis += v, ris += v, minn += v, added += v;}
}; struct dzpd <20> t;
int main()
{
scanf("%d%d",&n,&m);
for (i = 1; i <= n; i++) scanf("%d",a+i);
for (i = 1; (i << 1) <= n; i++)
{
c[min(a[i],a[n-i+1])+1]++;
c[max(a[i],a[n-i+1])+1]--;
}
for (i = 1; i <= a[(n+1)>>1]; i++)
fail[i] = fail[i-1] + c[i];
t.bd(1,a[(n+1)>>1]);
if (t.minn == 0) printf("%lld\n",t.ans);
else puts("0");
while(m--)
{
scanf("%d%d",&i,&k);
if (a[i] != a[n-i+1]) t.add(min(a[i],a[n-i+1])+1,max(a[i],a[n-i+1]),-1); a[i] = k;
if (a[i] != a[n-i+1]) t.add(min(a[i],a[n-i+1])+1,max(a[i],a[n-i+1]),+1);
if (t.minn == 0) printf("%lld\n",t.ans);
else puts("0");
}
return 0;
}