P12846 [蓝桥杯 2025 国 A] 翻转硬币 题解
『隔一个翻一个?隔两个翻一个?这咋做?』
「你先别考虑那么多啊,你会做直接翻转和查询的吗?」
『有啥难度,不就是 P3870 的线段树大法吗。』
「答对咯,就是线段树。那你能详细说下线段树怎么处理吗?」
『这很简单吧,除去正常线段树要维护的内容,再额外维护一下该区间正面朝上的硬币个数。要翻转,就让当前的正面朝上硬币个数被区间长度减一遍,其他都是板子吧。但是……?』
「但是这道题目还有隔一个和隔两个对不对,可以理解为
『哦……』
「
『完了,该不是要维护
「对,就是
『
「非常棒,那么下一个问题来了,你在维护的时候,需要处理哪些余数呢?」
『分类讨论一下吧。以
「对。之所以有三个不同余数,是因为这里只隔
『同理,以
「以及只剩下常规套路,全都翻转的那种,就每个余数遍历一遍就行啦!」
『对对。额,那么我还有最后一个问题,该如何快速确定某段区间中有哪些数的余数是某个值呢?』
「呀,这个应该是有标准数学公式的,不过我们可以用常数稍大一些的枚举法,枚举出起点终点就行了哦。从区间的左端点、右端点分别往中间靠拢嘛,余数不对就缩进嘛。」
『哦!我明白了。这样的常数也不算大呀,只有
「到这里,这题基本就结束了吧,你听懂了吗?」
『听是听懂了,但是 Talk is cheap , show me your code .,代码快快放出来哇 > < 』
「当然会放代码咯。不过我事先说好,这代码写的有点乱,某些内容可能是兀余的,可读性不一定好,将就看吧。」
#include<bits/stdc++.h>
#define LL long long
#define UInt unsigned int
#define ULL unsigned long long
#define LD long double
#define pii pair<int,int>
#define pLL pair<LL,LL>
#define pDD pair<LD,LD>
#define fr first
#define se second
#define pb push_back
#define isr insert
using namespace std;
const int N = 2e6+5;
int n,Q,a[N],bel[N];
struct SegTree{
int up,a[N],s[N],t[N];
int ls(int x){return (x<<1);}
int rs(int x){return (x<<1|1);}
void push_up(int x){s[x]=s[ls(x)]+s[rs(x)];return;}
void build(int x,int l,int r){
if(l==r){s[x]=a[l],t[x]=0;return;}
int mid=(l+r)>>1;
build(ls(x),l,mid);build(rs(x),mid+1,r);
push_up(x);return;
}
void push_down(int x,int l,int r){
if(!t[x])return;
t[ls(x)]^=1,t[rs(x)]^=1;
int mid=(l+r)>>1;
s[ls(x)]=(mid-l+1)-s[ls(x)];
s[rs(x)]=(r-mid)-s[rs(x)];
t[x]=0;return;
}
void change(int x,int l,int r,int L,int R){
if(r<L||R<l)return;
if(L<=l&&r<=R){s[x]=(r-l+1)-s[x],t[x]^=1;return;}
push_down(x,l,r);
int mid=(l+r)>>1;
change(ls(x),l,mid,L,R);
change(rs(x),mid+1,r,L,R);
push_up(x);return;
}
LL ask(int x,int l,int r,int L,int R){
if(r<L||R<l)return 0;
if(L<=l&&r<=R)return s[x];
push_down(x,l,r);
int mid=(l+r)>>1;
return ask(ls(x),l,mid,L,R)+ask(rs(x),mid+1,r,L,R);
}
}SegT[7];
int read(){
int su=0,pp=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')pp=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){su=su*10+ch-'0';ch=getchar();}
return su*pp;
}
int main(){
n=read(),Q=read();
for(int i=1;i<=n;i++){
int x=read();
bel[i]=(!(i%6)?6:(i%6));
SegT[bel[i]].a[++SegT[bel[i]].up]=x;
}
for(int i=1;i<=6;i++)
SegT[i].up=n/i+((n%i)<=i),
SegT[i].build(1,1,SegT[i].up);
while(Q--){
int opt=read(),x=read(),y=read();
if(opt==1){
int ys=(x%6);
for(int c=1;c<=3;c++,ys=(ys+2)%6){
int st=x;
while(st%6!=ys&&st<=y)st++;
if(st>y)continue;
int ed=y;
while(ed%6!=ys&&ed>st)ed--;
if(!ys)ys=6;
st=(st+5)/6,ed=(ed+5)/6;
SegT[ys].change(1,1,SegT[ys].up,st,ed);
}
}else if(opt==2){
int ys=(x%6);
for(int c=1;c<=2;c++,ys=(ys+3)%6){
int st=x;
while(st%6!=ys&&st<=y)st++;
if(st>y)continue;
int ed=y;
while(ed%6!=ys&&ed>st)ed--;
if(!ys)ys=6;
st=(st+5)/6,ed=(ed+5)/6;
SegT[ys].change(1,1,SegT[ys].up,st,ed);
}
}else if(opt==3){
int ys=(x%6);
for(int c=1;c<=6;c++,ys=(ys+1)%6){
int st=x;
while(st%6!=ys&&st<=y)st++;
if(st>y)continue;
int ed=y;
while(ed%6!=ys&&ed>st)ed--;
if(!ys)ys=6;
st=(st+5)/6,ed=(ed+5)/6;
SegT[ys].change(1,1,SegT[ys].up,st,ed);
}
}else{
int res=0,ys=(x%6);
for(int c=1;c<=6;c++,ys=(ys+1)%6){
int st=x;
while(st%6!=ys&&st<=y)st++;
if(st>y)continue;
int ed=y;
while(ed%6!=ys&&ed>st)ed--;
if(!ys)ys=6;
st=(st+5)/6,ed=(ed+5)/6;
res+=SegT[ys].ask(1,1,SegT[ys].up,st,ed);
}cout<<res<<"\n";
}
}
return 0;
}
『你这下面几个部分都是复制粘贴的呀!就差一个数字呢!我可以帮你压得短短的!』
「我知道啦,但是看到这里还不点赞的朋友们真的很残忍哦……麻烦留个赞吧,求求了喵!」