1 条题解
-
0
自动搬运
来自洛谷,原作者为

Noby_Glds
be serious搬运于
2025-08-24 22:26:49,当前版本为作者最后更新于2022-05-09 13:15:02,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
这是我第一个不看任何题解过的蓝题。
我想把自己曲折的思考过程分享给大家。
一起深度剖析这道题。
思路
一看标签:模拟,线段树。朴实无华。
看了一下题目,很明显是让我们先找一个通解。
它是如何消除一个反面朝上的硬币的?
凡事先从简单做起,考虑只有一个硬币反面朝上。
HHHTH很显然,想让那个反面朝上的硬币反过来,就得存在 个反面朝上的硬币。
所以第一步,是把前面 个硬币一起反过来,需要 步。
TTTTH接下来就可以把所有反面朝上的硬币全翻回来了,共需 步。
HHHHH 完成如果我们令 为这个反面朝上的硬币的位置,答案就为:
如果有两个甚至更多的硬币反面朝上呢?
HHHHTT在这里, 的值一开始不是 ,而是 。
所以我们会把位置为 ,, 的硬币翻过来,位置为 的硬币没有动。
HTTTTT接着, 退回 ,并把所经过的位置上的硬币翻面。
HHHHHT然后就只有一个硬币反面朝上了。
我们在分析过程中可以发现,若 为反面朝上的硬币的数量,则在翻硬币时, 会不断增加,直到位置 上的硬币为反面,然后 会不断减少,直到退回 原来的值。
简的来说,就是 不断往右移,碰到一个反面朝上的硬币则折回,向左移回到原来的位置,这个反面朝上的硬币就消除了。
这样不断地消去,直到没有反面朝上的硬币。
无解的情况怎么判断?
无解的情况只有一种,就是 不断向右移,却一直没碰到反面朝上的硬币。
也就是说,在位置 的左边有 个硬币反面朝上。
可左边一共就 个位置!
所以令我们惊异的是,无解的情况不存在!
坏坏的出题人。答案公式?
我们模拟一下这个场景。
此时一共有 个硬币反面朝上, 的值目前为 。
往右移,碰到一个反面朝上的硬币,再折回来。
若这个硬币的位置为 ,则这个操作所用步数为:
然后 值减 ,但我们不让它减,让它在接下来的操作中体现,此时 的初始值更新为 。
接着又碰到一个反面朝上的硬币,若这个硬币的位置为 ,则这个操作所用步数为:
以此类推。
把所有的操作的步数推出来后,相加得:
$$2*(w_1+w_2+...+w_{m-1}+w_m)-2(m+(m-1)+...+2+1)+m*1 $$所以,我们做到了……
等等,怎么维护 和 ?
看到这种区间异或的题目,我们会想到P3870 [TJOI2009] 开关这道题。
先切了这道题,拿着 AC 的线段树代码,公式一套,就完成啦!
代码
#include<bits/stdc++.h> #define N 1000010 #define int long long//不开 long long 见祖宗 using namespace std; struct hhh{ int l,r,cnt,sum,lz; }dl[N*4]; int n,m,l,r; int a[N]; char p; void pushup(int bh){ dl[bh].cnt=dl[bh*2].cnt+dl[bh*2+1].cnt; dl[bh].sum=dl[bh*2].sum+dl[bh*2+1].sum; } void pushdown(int bh){ if(!dl[bh].lz) return; dl[bh*2].lz^=1; dl[bh*2].cnt=dl[bh*2].r-dl[bh*2].l+1-dl[bh*2].cnt; dl[bh*2].sum=(dl[bh*2].r+dl[bh*2].l)*(dl[bh*2].r-dl[bh*2].l+1)/2-dl[bh*2].sum; dl[bh*2+1].lz^=1; dl[bh*2+1].cnt=dl[bh*2+1].r-dl[bh*2+1].l+1-dl[bh*2+1].cnt; dl[bh*2+1].sum=(dl[bh*2+1].r+dl[bh*2+1].l)*(dl[bh*2+1].r-dl[bh*2+1].l+1)/2-dl[bh*2+1].sum; dl[bh].lz=0; } void build(int bh,int l,int r){ dl[bh].l=l,dl[bh].r=r; if(l==r){ if(a[l]) dl[bh].cnt=1,dl[bh].sum=l; return; } int mid=(l+r)>>1; build(bh*2,l,mid),build(bh*2+1,mid+1,r); pushup(bh); } void update(int bh,int L,int R){ int l=dl[bh].l,r=dl[bh].r; if(l>=L&&r<=R){ dl[bh].lz^=1; dl[bh].cnt=r-l+1-dl[bh].cnt; dl[bh].sum=(r+l)*(r-l+1)/2-dl[bh].sum; return; } pushdown(bh); int mid=(l+r)>>1; if(L<=mid) update(bh*2,L,R); if(R>mid) update(bh*2+1,L,R); pushup(bh); } signed main(){ ios::sync_with_stdio(0); cin.tie(0),cout.tie(0); cin>>n>>m; for(int i=1;i<=n;i++){ cin>>p; if(p=='T') a[i]=1; } build(1,1,n); cout<<2*dl[1].sum-dl[1].cnt*dl[1].cnt<<endl; while(m--){ cin>>l>>r; update(1,l,r); cout<<2*dl[1].sum-dl[1].cnt*dl[1].cnt<<endl; } return 0; }//由于我用的是cin和cout,这道题卡常又卡得紧,所以要开O2后记
这篇题解注重于寻找通解的过程,而不是线段树怎么打。
如果有茬,欢迎向我私信。
QAQ。
- 1
信息
- ID
- 4608
- 时间
- 2000ms
- 内存
- 128MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者