九下四月下旬日记
4.21
闲话
- 文综也是写两张卷,一个半小时写完了。政治的二卷和历史的一卷过于抽象,难评。
- 晚上开班会。讲了讲中考体育和信息上课的注意事项;讲了下 熊猫杯事件 ;放了 《CCTV-10讲述》 20110831 子弹飞 ;喂了点鸡汤。
- 回宿舍后拿到了原班的班服,衣服质量堪忧,但原班主任不让穿。
做题纪要
4.22
闲话
- 机房开空调了,但教室、宿舍还不让开。
- \(miaomiao\) 问我们学没学平衡树。然后催了催做 1.字符串专题 的题,称拖了太长时间了,但剩下的题需要的知识点还没学。
- @Estelle_N , @5k_sync_closer , @Joy_Dream_Glory , @int_R , @hh弟中弟 , @lxyt_415x 被 \(huge\) 和 \(miaomiao\) 拉来 \(4\) 机房了。 \(miaomiao\) 给他们修了修电脑(因为 【数据删除】 所以把后排网全关了) ,然后给他们开了 luogu 。这下机房一半有 luogu ,一半没有 luogu 。
- \(huge\) 因我们一直在咯咯笑,把我们 \(D\) 了。
- 去楼下转了一圈,发现原班主任在上信息课,貌似两节课是“连堂”,不是“联排”。
- 因安排上信息课占了明天第 \(6,7\) 节奥赛课,所以用今天和明天的晚三补了。
- 晚三来的时候看见三楼 \(2,3\) 机房和四楼 \(6\) 机房亮着灯,年级部晚上甚至都安排了课;以前一直以为只有两个信息老师。上楼的路上发现只有四、五楼的楼梯声控灯是亮着的,剩下的声控灯是黑着的,安全隐患不小。
- 晚二见基本不下雨了就没带雨伞去机房了,但晚三雨突然下得特别大, \(feifei\) 见状从三机房拿出一个小行李箱,里面装满了“来自学长的馈赠”的雨伞,然后就把雨伞“借”给我们用了,让我们记得还。
做题纪要
luogu P1494 [国家集训队] 小 Z 的袜子
-
设 \([l,r]\) 内每个数出现的次数为 \(cnt_{i}\) ,有 \(\begin{aligned} \frac{\sum\limits_{i=1}^{\max\limits_{j=1}^{n} \{ c_{j} \}} \binom{cnt_{i}}{2}}{\binom{r-l+1}{2}} \end{aligned}\) 即为所求。
点击查看代码
ll a[50010],cnt[50010],C[50010],L[50010],R[50010],pos[50010],klen,ksum; pair<ll,ll>ans[50010]; struct ask { ll l,r,id; }q[50010]; bool q_cmp(ask a,ask b) { return (pos[a.l]==pos[b.l])?((pos[a.l]%2==1)?(a.r<b.r):(a.r>b.r)):(a.l<b.l); } void init(ll n,ll m) { klen=n/sqrt(m)+1; ksum=n/klen; for(ll i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(ll i=1;i<=ksum;i++) { for(ll j=L[i];j<=R[i];j++) { pos[j]=i; } } } void add(ll x,ll &sum) { sum-=C[cnt[a[x]]]; cnt[a[x]]++; sum+=C[cnt[a[x]]]; } void del(ll x,ll &sum) { sum-=C[cnt[a[x]]]; cnt[a[x]]--; sum+=C[cnt[a[x]]]; } ll gcd(ll a,ll b) { return b?gcd(b,a%b):a; } int main() { ll n,m,l=1,r=0,sum=0,d,i; cin>>n>>m; for(i=1;i<=n;i++) { cin>>a[i]; C[i]=(i>=2)?i*(i-1)/2:0; } init(n,m); for(i=1;i<=m;i++) { cin>>q[i].l>>q[i].r; q[i].id=i; } sort(q+1,q+1+m,q_cmp); for(i=1;i<=m;i++) { while(l>q[i].l) { l--; add(l,sum); } while(r<q[i].r) { r++; add(r,sum); } while(l<q[i].l) { del(l,sum); l++; } while(r>q[i].r) { del(r,sum); r--; } ans[q[i].id]=make_pair(sum,(q[i].r-q[i].l+1)*(q[i].r-q[i].l+1-1)/2); } for(i=1;i<=m;i++) { d=gcd(ans[i].first,ans[i].second); if(d==0) { cout<<"0/1"<<endl; } else { cout<<ans[i].first/d<<"/"<<ans[i].second/d<<endl; } } return 0; }
luogu P1903 [国家集训队] 数颜色 / 维护队列
-
多倍经验: BZOJ2453 维护队列
-
莫队带修改板子。
- 对 \([l,r]\) 强行加上一维时间维 \(time\) 使其变成 \([l,r,time]\) ,表示这次操作的时间,其中时间维 \(time\) 表示经历的修改次数。
- 排序方式为以 \(l\) 所在的块的编号为第一关键字,以 \(r\) 所在的块的编号为第二关键字,以 \(time\) 为第三关键字升序排序。
- 设序列长为 \(n\) ,共有 \(m\) 个询问和 \(t\) 个修改 。块长取 \(\frac{n^{\frac{2}{3}}t^{\frac{1}{3}}}{m^{\frac{1}{3}}}\) 。假设 \(m,t\) 同阶,块长一般取 \(n^{\frac{2}{3}}\) 。
点击查看代码
int a[1000010],cnt[1000010],L[1000010],R[1000010],pos[1000010],ans[1000010],klen,ksum; struct ask { int l,r,id,tim; }q[1000010]; struct change { int pos,col; }c[1000010]; bool q_cmp(ask a,ask b) { return (pos[a.l]==pos[b.l])?((pos[a.r]==pos[b.r])?(a.tim<b.tim):(a.r<b.r)):(a.l<b.l); } void init(int n) { klen=pow(n,2.0/3); ksum=n/klen; for(int i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(int i=1;i<=ksum;i++) { for(int j=L[i];j<=R[i];j++) { pos[j]=i; } } } void add(int x,int &sum) { cnt[x]++; sum+=(cnt[x]==1); } void del(int x,int &sum) { cnt[x]--; sum-=(cnt[x]==0); } int main() { int n,m,l=1,r=0,tim=0,sum=0,qcnt=0,ccnt=0,i; char pd; cin>>n>>m; for(i=1;i<=n;i++) { cin>>a[i]; } init(n); for(i=1;i<=m;i++) { cin>>pd; if(pd=='Q') { qcnt++; cin>>q[qcnt].l>>q[qcnt].r; q[qcnt].id=qcnt; q[qcnt].tim=ccnt; } else { ccnt++; cin>>c[ccnt].pos>>c[ccnt].col; } } sort(q+1,q+1+qcnt,q_cmp); for(i=1;i<=qcnt;i++) { while(l>q[i].l) { l--; add(a[l],sum); } while(r<q[i].r) { r++; add(a[r],sum); } while(l<q[i].l) { del(a[l],sum); l++; } while(r>q[i].r) { del(a[r],sum); r--; } while(tim<q[i].tim)//加上修改操作 { tim++; if(l<=c[tim].pos&&c[tim].pos<=r)//如果在区间内则加上贡献 { del(a[c[tim].pos],sum); add(c[tim].col,sum); } swap(a[c[tim].pos],c[tim].col); } while(tim>q[i].tim)//对修改进行还原 { if(l<=c[tim].pos&&c[tim].pos<=r)//如果在区间内则加上贡献 { del(a[c[tim].pos],sum); add(c[tim].col,sum); } swap(a[c[tim].pos],c[tim].col); tim--; } ans[q[i].id]=sum; } for(i=1;i<=qcnt;i++) { cout<<ans[i]<<endl; } return 0; }
luogu P3709 大爷的字符串题
-
题意实际是问从 \([l,r]\) 中每次取出一个单调递增序列,取完整个区间需要多少次,即区间众数的出现次数。
-
设 \([l,r]\) 内每个数出现的次数为 \(cnt_{i}\) ,等于 \(cnt_{i}\) 的有 \(num_{cnt_{i}}\) 个数,然后进行转移。
点击查看代码
int a[200010],b[200010],num[200010],cnt[200010],L[200010],R[200010],pos[200010],ans[200010],klen,ksum; struct ask { int l,r,id; }q[200010]; bool q_cmp(ask a,ask b) { return (pos[a.l]==pos[b.l])?((pos[a.l]%2==1)?(a.r<b.r):(a.r>b.r)):(a.l<b.l); } void init(int n,int m) { klen=n/sqrt(m)+1; ksum=n/klen; for(int i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(int i=1;i<=ksum;i++) { for(int j=L[i];j<=R[i];j++) { pos[j]=i; } } } void add(int x,int &sum) { num[cnt[a[x]]]--; cnt[a[x]]++; num[cnt[a[x]]]++; sum=max(sum,cnt[a[x]]); } void del(int x,int &sum) { num[cnt[a[x]]]--; sum-=(sum==cnt[a[x]]&&num[cnt[a[x]]]==0);//如果不是当前的数,则保留答案 cnt[a[x]]--; num[cnt[a[x]]]++; } int main() { int n,m,l=1,r=0,sum=0,i; cin>>n>>m; for(i=1;i<=n;i++) { cin>>a[i]; b[i]=a[i]; } sort(b+1,b+1+n); b[0]=unique(b+1,b+1+n)-(b+1); for(i=1;i<=n;i++) { a[i]=lower_bound(b+1,b+1+b[0],a[i])-b; } init(n,m); for(i=1;i<=m;i++) { cin>>q[i].l>>q[i].r; q[i].id=i; } sort(q+1,q+1+m,q_cmp); for(i=1;i<=m;i++) { while(l>q[i].l) { l--; add(l,sum); } while(r<q[i].r) { r++; add(r,sum); } while(l<q[i].l) { del(l,sum); l++; } while(r>q[i].r) { del(r,sum); r--; } ans[q[i].id]=-sum; } for(i=1;i<=m;i++) { cout<<ans[i]<<endl; } return 0; }
BZOJ2906 颜色
-
查询时枚举颜色分别进行统计答案,时间复杂度会爆炸。
-
类似 luogu P4168 [Violet]蒲公英 预处理每块内每个数出现的次数和此时产生的贡献,并使用前缀和优化。
-
由 \((cnt_{i}+1)^{2}-cnt_{i}^{2}=2cnt_{i}+1\) 对散块进行统计答案。
-
块长取 \(n^{\frac{2}{3}}\) 。
点击查看代码
int a[50010],L[50010],R[50010],pos[50010],cnt[60][60][50010],f[60][60][50010],ls[50010],klen,ksum; void init(int n,int m) { klen=pow(n,2.0/3); ksum=n/klen; for(int i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(int i=1;i<=ksum;i++) { for(int j=L[i];j<=R[i];j++) { pos[j]=i; cnt[i][i][a[j]]++; } for(int j=1;j<=m;j++) { f[i][i][j]=f[i][i][j-1]+cnt[i][i][j]*cnt[i][i][j]; } } for(int i=1;i<=ksum;i++) { for(int j=i+1;j<=ksum;j++) { for(int k=1;k<=m;k++) { cnt[i][j][k]=cnt[i][j-1][k]+cnt[j][j][k]; f[i][j][k]=f[i][j][k-1]+cnt[i][j][k]*cnt[i][j][k]; } } } } int query(int l,int r,int x,int y) { int ans=0; memset(ls,0,sizeof(ls)); if(pos[l]==pos[r]) { for(int i=l;i<=r;i++) { ans+=(x<=a[i]&&a[i]<=y)*(ls[a[i]]*2+1); ls[a[i]]++; } } else { ans=f[pos[l]+1][pos[r]-1][y]-f[pos[l]+1][pos[r]-1][x-1]; for(int i=l;i<=R[pos[l]];i++) { ls[a[i]]=cnt[pos[l]+1][pos[r]-1][a[i]]; } for(int i=L[pos[r]];i<=r;i++) { ls[a[i]]=cnt[pos[l]+1][pos[r]-1][a[i]]; } for(int i=l;i<=R[pos[l]];i++) { ans+=(x<=a[i]&&a[i]<=y)*(ls[a[i]]*2+1); ls[a[i]]++; } for(int i=L[pos[r]];i<=r;i++) { ans+=(x<=a[i]&&a[i]<=y)*(ls[a[i]]*2+1); ls[a[i]]++; } } return ans; } int main() { int n,m,q,l,r,x,y,ans=0,i; cin>>n>>m>>q; for(i=1;i<=n;i++) { cin>>a[i]; } init(n,m); for(i=1;i<=q;i++) { cin>>l>>r>>x>>y; l^=ans; r^=ans; x^=ans; y^=ans; ans=query(l,r,x,y);//其实需要对l,r,x,y的值进行判断 cout<<ans<<endl; } return 0; }
CF86D Powerful array
-
莫队直接处理即可。
点击查看代码
ll a[1000010],cnt[1000010],ans[1000010],L[1000010],R[1000010],pos[1000010],klen,ksum; struct ask { ll l,r,id; }q[1000010]; bool q_cmp(ask a,ask b) { return (pos[a.l]==pos[b.l])?((pos[a.l]%2==1)?(a.r<b.r):(a.r>b.r)):(a.l<b.l); } void init(ll n,ll m) { klen=n/sqrt(m)+1; ksum=n/klen; for(ll i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(ll i=1;i<=ksum;i++) { for(ll j=L[i];j<=R[i];j++) { pos[j]=i; } } } void add(ll x,ll &sum) { sum-=cnt[a[x]]*cnt[a[x]]*a[x]; cnt[a[x]]++; sum+=cnt[a[x]]*cnt[a[x]]*a[x]; } void del(ll x,ll &sum) { sum-=cnt[a[x]]*cnt[a[x]]*a[x]; cnt[a[x]]--; sum+=cnt[a[x]]*cnt[a[x]]*a[x]; } int main() { ll n,m,l=1,r=0,sum=0,i; cin>>n>>m; for(i=1;i<=n;i++) { cin>>a[i]; } init(n,m); for(i=1;i<=m;i++) { cin>>q[i].l>>q[i].r; q[i].id=i; } sort(q+1,q+1+m,q_cmp); for(i=1;i<=m;i++) { while(l>q[i].l) { l--; add(l,sum); } while(r<q[i].r) { r++; add(r,sum); } while(l<q[i].l) { del(l,sum); l++; } while(r>q[i].r) { del(r,sum); r--; } ans[q[i].id]=sum; } for(i=1;i<=m;i++) { cout<<ans[i]<<endl; } return 0; }
4.23
闲话
- 因为学校的排水系统十分发达,所以早上正常集合加跑操。
- 下午先来了趟机房拿笔记本和水杯,然后看见了原班主任在 \(5\) 楼饮水器旁接水,和她打了个招呼。
- 到 \(3\) 楼 \(2\) 机房上信息课,发现信息老师不是原班主任,但比较抽象,包括但不限于将左手小指放在左
Ctrl
的位置,食指放在F
的位置(合着就我放在D
的位置是吧?)。原来一共有四个信息老师。发现能上 ftp ,然后把教师文件传到了 ftp 里了。 - 下午 @K8He 和 @jijidawang 因中考体育的事情,从梦熊回来了,然后他们也有了 luogu 。机房顿时欢乐了不少。然后就被 \(miaomiao\) \(D\) 了。
- 晚上到机房的时候看见 \(huge\) 在默写《兰亭集序》,据 @Vsinger_洛天依 所说上次见他还是在默写《岳阳楼记》。
做题纪要
luogu P4396 [AHOI2013] 作业
-
对值域进行分块,然后维护权值 \(\in[a,b]\) 的数的个数和种类数。
点击查看代码
int a[1000010],cnt[1000010],L[1000010],R[1000010],pos[1000010],sum[1000010][2],klen,ksum; pair<int,int>ans[1000010]; struct ask { int l,r,a,b,id; }q[1000010]; bool q_cmp(ask a,ask b) { return (pos[a.l]==pos[b.l])?((pos[a.l]%2==1)?(a.r<b.r):(a.r>b.r)):(a.l<b.l); } void init(int n,int m) { klen=n/sqrt(m)+1; ksum=n/klen; for(int i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(int i=1;i<=ksum;i++) { for(int j=L[i];j<=R[i];j++) { pos[j]=i; } } } void update(int l,int val) { cnt[l]+=val; sum[pos[l]][0]+=val; if(val==1) { sum[pos[l]][1]+=(cnt[l]==1); } else { sum[pos[l]][1]-=(cnt[l]==0); } } pair<int,int> query(int l,int r) { int ans1=0,ans2=0; if(pos[l]==pos[r]) { for(int i=l;i<=r;i++) { ans1+=cnt[i]; ans2+=(cnt[i]!=0); } } else { for(int i=l;i<=R[pos[l]];i++) { ans1+=cnt[i]; ans2+=(cnt[i]!=0); } for(int i=pos[l]+1;i<=pos[r]-1;i++) { ans1+=sum[i][0]; ans2+=sum[i][1]; } for(int i=L[pos[r]];i<=r;i++) { ans1+=cnt[i]; ans2+=(cnt[i]!=0); } } return make_pair(ans1,ans2); } void add(int x) { update(a[x],1); } void del(int x) { update(a[x],-1); } int main() { int n,m,l=1,r=0,i; cin>>n>>m; for(i=1;i<=n;i++) { cin>>a[i]; } init(n,m); for(i=1;i<=m;i++) { cin>>q[i].l>>q[i].r>>q[i].a>>q[i].b; q[i].id=i; } sort(q+1,q+1+m,q_cmp); for(i=1;i<=m;i++) { while(l>q[i].l) { l--; add(l); } while(r<q[i].r) { r++; add(r); } while(l<q[i].l) { del(l); l++; } while(r>q[i].r) { del(r); r--; } ans[q[i].id]=query(q[i].a,q[i].b); } for(i=1;i<=m;i++) { cout<<ans[i].first<<" "<<ans[i].second<<endl; } return 0; }
luogu P4867 Gty的妹子序列
-
卡空间,需要优化分块的 \(L,R,pos\) 数组。
点击查看代码
int n,a[100010],cnt[1000010],ans[1000010],sum[1000010],klen,ksum; #define pos(x) ((int)(ceil(1.0*x/klen))) #define L(x) ((x-1)*klen+1) #define R(x) (min(x*klen,n)) struct ask { int l,r,a,b,id; }q[1000010]; bool q_cmp(ask a,ask b) { return (pos(a.l)==pos(b.l))?((pos(a.l)%2==1)?(a.r<b.r):(a.r>b.r)):(a.l<b.l); } void init(int n,int m) { klen=n/sqrt(m)+1; ksum=n/klen; } void update(int l,int val) { cnt[l]+=val; if(val==1) { sum[pos(l)]+=(cnt[l]==1); } else { sum[pos(l)]-=(cnt[l]==0); } } int query(int l,int r) { int ans=0; if(pos(l)==pos(r)) { for(int i=l;i<=r;i++) { ans+=(cnt[i]!=0); } } else { for(int i=l;i<=R(pos(l));i++) { ans+=(cnt[i]!=0); } for(int i=pos(l)+1;i<=pos(r)-1;i++) { ans+=sum[i]; } for(int i=L(pos(r));i<=r;i++) { ans+=(cnt[i]!=0); } } return ans; } void add(int x) { update(a[x],1); } void del(int x) { update(a[x],-1); } int main() { int m,l=1,r=0,i; cin>>n>>m; for(i=1;i<=n;i++) { cin>>a[i]; } init(n,m); for(i=1;i<=m;i++) { cin>>q[i].l>>q[i].r>>q[i].a>>q[i].b; q[i].id=i; } sort(q+1,q+1+m,q_cmp); for(i=1;i<=m;i++) { while(l>q[i].l) { l--; add(l); } while(r<q[i].r) { r++; add(r); } while(l<q[i].l) { del(l); l++; } while(r>q[i].r) { del(r); r--; } ans[q[i].id]=query(q[i].a,q[i].b); } for(i=1;i<=m;i++) { cout<<ans[i]<<endl; } return 0; }
[ABC293G] Triple Index
-
设 \([l,r]\) 内每个数出现的次数为 \(cnt_{i}\) ,有 \(\begin{aligned} \sum\limits_{i=1}^{\max\limits_{j=1}^{n} \{ a_{j} \}} \binom{cnt_{i}}{3} \end{aligned}\) 即为所求。
点击查看代码
ll a[200010],cnt[200010],ans[200010],C[200010],L[200010],R[200010],pos[200010],klen,ksum; struct ask { ll l,r,id; }q[200010]; bool q_cmp(ask a,ask b) { return (pos[a.l]==pos[b.l])?((pos[a.l]%2==1)?(a.r<b.r):(a.r>b.r)):(a.l<b.l); } void init(ll n,ll m) { klen=n/sqrt(m)+1; ksum=n/klen; for(ll i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(ll i=1;i<=ksum;i++) { for(ll j=L[i];j<=R[i];j++) { pos[j]=i; } } } void add(ll x,ll &sum) { sum-=C[cnt[a[x]]]; cnt[a[x]]++; sum+=C[cnt[a[x]]]; } void del(ll x,ll &sum) { sum-=C[cnt[a[x]]]; cnt[a[x]]--; sum+=C[cnt[a[x]]]; } int main() { ll n,m,l=1,r=0,sum=0,i; cin>>n>>m; for(i=1;i<=n;i++) { cin>>a[i]; C[i]=(i>=3)?i*(i-1)*(i-2)/6:0; } init(n,m); for(i=1;i<=m;i++) { cin>>q[i].l>>q[i].r; q[i].id=i; } sort(q+1,q+1+m,q_cmp); for(i=1;i<=m;i++) { while(l>q[i].l) { l--; add(l,sum); } while(r<q[i].r) { r++; add(r,sum); } while(l<q[i].l) { del(l,sum); l++; } while(r>q[i].r) { del(r,sum); r--; } ans[q[i].id]=sum; } for(i=1;i<=m;i++) { cout<<ans[i]<<endl; } return 0; }
CF220B Little Elephant and Array
-
离散化后莫队维护即可。
点击查看代码
int a[100010],b[100010],d[100010],cnt[100010],ans[100010],L[100010],R[100010],pos[100010],klen,ksum; struct ask { int l,r,id; }q[100010]; bool q_cmp(ask a,ask b) { return (pos[a.l]==pos[b.l])?((pos[a.l]%2==1)?(a.r<b.r):(a.r>b.r)):(a.l<b.l); } void init(int n,int m) { klen=n/sqrt(m)+1; ksum=n/klen; for(int i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(int i=1;i<=ksum;i++) { for(int j=L[i];j<=R[i];j++) { pos[j]=i; } } } void add(int x,int &sum) { sum-=(cnt[a[x]]==d[a[x]]); cnt[a[x]]++; sum+=(cnt[a[x]]==d[a[x]]); } void del(int x,int &sum) { sum-=(cnt[a[x]]==d[a[x]]); cnt[a[x]]--; sum+=(cnt[a[x]]==d[a[x]]); } int main() { int n,m,ls,l=1,r=0,sum=0,i; cin>>n>>m; for(i=1;i<=n;i++) { cin>>a[i]; b[i]=a[i]; } init(n,m); sort(b+1,b+1+n); b[0]=unique(b+1,b+1+n)-(b+1); for(i=1;i<=n;i++) { ls=lower_bound(b+1,b+1+b[0],a[i])-b; d[ls]=a[i]; a[i]=ls; } for(i=1;i<=m;i++) { cin>>q[i].l>>q[i].r; q[i].id=i; } sort(q+1,q+1+m,q_cmp); for(i=1;i<=m;i++) { while(l>q[i].l) { l--; add(l,sum); } while(r<q[i].r) { r++; add(r,sum); } while(l<q[i].l) { del(l,sum); l++; } while(r>q[i].r) { del(r,sum); r--; } ans[q[i].id]=sum; } for(i=1;i<=m;i++) { cout<<ans[i]<<endl; } return 0; }
[JOISC 2014 Day1] 歴史の研究
-
像 luogu P3709 大爷的字符串题 这样再次记录此时答案的来源不是很可做。
- 增加操作容易实现,但删除操作难以实现。
-
只加不减回滚莫队板子。
- 考虑只进行一个操作,剩下的交给回滚解决。
- 仍对询问进行分块,排序方式为以 \(l\) 所在的块的编号为第一关键字升序排序,以 \(r\) 为第二关键字升序排序。
- 按顺序处理询问。
- 若当前询问的左右端点所属的块相同,则暴力统计答案,回答询问,并及时消除对临时数组的影响。
- 若当前询问的左右端点所属的块不同,
- 若当前询问左端点所属块 \(block\) 和上一个左右端点所属的块不同的询问的左端点所属块 \(lastblock\) 不同,则将莫队区间的 \(l\) 指针初始化为 \(block\) 的右端点加 \(1\) , \(r\) 指针的右端点初始化为 \(block\) 的右端点,使其变成一个空区间。
- 若 \(r\) 指针小于当前询问的右端点,则向右不断扩展 \(r\) 指针,直至 \(r\) 等于当前询问的右端点。
- 若 \(l\) 指针大于当前询问的左端点,则向左不断扩展 \(l\) 指针,直至 \(l\) 等于当前询问的左端点。
- 在回答当前询问后撤销左端点的移动,使 \(l\) 指针回滚到 \(block\) 的右端点加 \(1\) 。
- 即对于左右端点不在一个块的询问, \(l\) 始终等于 \(block\) 的右端点加 \(1\) 。
- 一些注意事项
- 暴力统计答案和回滚操作时,均要使用额外的临时变量,以避免对下次询问产生不必要的影响。
- 初始化 \(l\) 和 \(r\) 指针必须对于于左右端点不在一个块的询问,一共至少要进行一次,仅用当前询问的左端点所在的块和上一个询问的左端点进行比较是错误的。
- 块长取 \(\frac{n}{\sqrt{m}}\) 。
点击查看代码
ll a[100010],b[100010],d[100010],cnt[100010],lscnt[100010],ans[100010],L[100010],R[100010],pos[100010],klen,ksum; struct ask { ll l,r,id; }q[100010]; bool q_cmp(ask a,ask b) { return (pos[a.l]==pos[b.l])?(a.r<b.r):(a.l<b.l); } void init(ll n,ll m) { klen=n/sqrt(m)+1; ksum=n/klen; for(ll i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(ll i=1;i<=ksum;i++) { for(ll j=L[i];j<=R[i];j++) { pos[j]=i; } } } void add(ll cnt[],ll x,ll &tmp) { cnt[a[x]]++; tmp=max(tmp,cnt[a[x]]*d[a[x]]); } void del(ll cnt[],ll x) { cnt[a[x]]--; } int main() { ll n,m,ls,l=1,r=0,sum,tmp=0,lastblock=0,i,j; cin>>n>>m; for(i=1;i<=n;i++) { cin>>a[i]; b[i]=a[i]; } init(n,m); sort(b+1,b+1+n); b[0]=unique(b+1,b+1+n)-(b+1); for(i=1;i<=n;i++) { ls=lower_bound(b+1,b+1+b[0],a[i])-b; d[ls]=a[i]; a[i]=ls; } for(i=1;i<=m;i++) { cin>>q[i].l>>q[i].r; q[i].id=i; } sort(q+1,q+1+m,q_cmp); for(i=1;i<=m;i++) { if(pos[q[i].l]==pos[q[i].r]) { sum=0; for(j=q[i].l;j<=q[i].r;j++) { add(lscnt,j,sum); } for(j=q[i].l;j<=q[i].r;j++) { del(lscnt,j); } } else { if(lastblock!=pos[q[i].l]) { while(r>R[pos[q[i].l]]) { del(cnt,r); r--; } while(l<R[pos[q[i].l]]+1) { del(cnt,l); l++; } tmp=0; lastblock=pos[q[i].l]; } while(r<q[i].r) { r++; add(cnt,r,tmp); } sum=tmp; while(l>q[i].l) { l--; add(cnt,l,sum); } while(l<R[pos[q[i].l]]+1)//回滚 { del(cnt,l); l++; } } ans[q[i].id]=sum; } for(i=1;i<=m;i++) { cout<<ans[i]<<endl; } return 0; }
4.24
闲话
- 经过现班主任和 \(miaomiao\) 的交涉,最终 @wkh2008 和 @xrlong 周三到周五被赶回教室学 \(whk\) 了。
- 详见 2024 HE中考 游记 4.24 。
做题纪要
HDU6333 Problem B. Harvest of Apples
-
分别令 \(l,r\) 表示题目中所给的 \(n,m\) ,则 \(\sum\limits_{i=0}^{r}\dbinom{l}{i}\) 即为所求。
-
由 \(\begin{aligned} \sum\limits_{i=0}^{r}\dbinom{l+1}{i}-\sum\limits_{i=0}^{r}\dbinom{l}{i} &=\sum\limits_{i=0}^{r}(\dbinom{l}{i}+\dbinom{l}{i-1})-\sum\limits_{i=0}^{r}\dbinom{l}{i} \\ &=\sum\limits_{i=0}^{r}\dbinom{l}{i-1} \\ &=\sum\limits_{i=0}^{r-1}\dbinom{l}{i} \\ &=\sum\limits_{i=0}^{r}\dbinom{l}{i}-\dbinom{l}{r} \end{aligned}\) 有 \(\begin{cases} \sum\limits_{i=0}^{r}\dbinom{l}{i}=2\sum\limits_{i=0}^{r}\dbinom{l-1}{i}-\dbinom{l-1}{r} \\ \sum\limits_{i=0}^{r}\dbinom{l}{i}=\frac{\sum\limits_{i=0}^{r}\binom{l+1}{i}+\binom{l}{r}}{2} \\ \sum\limits_{i=0}^{r}\dbinom{l}{i}=\sum\limits_{i=0}^{r-1}\dbinom{l}{i}+\dbinom{l}{r} \\ \sum\limits_{i=0}^{r}\dbinom{l}{i}=\sum\limits_{i=0}^{r+1}\dbinom{l}{i}-\dbinom{l}{r+1} \end{cases}\) 。
-
莫队维护即可。
点击查看代码
const ll p=1000000007; ll inv[100010],jc[100010],jc_inv[100010],ans[100010],L[100010],R[100010],pos[100010],klen,ksum; struct ask { ll l,r,id; }q[100010]; bool q_cmp(ask a,ask b) { return (pos[a.l]==pos[b.l])?((pos[a.l]%2==1)?(a.r<b.r):(a.r>b.r)):(a.l<b.l); } void init(ll n,ll m) { klen=n/sqrt(m)+1; ksum=n/klen; for(ll i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(ll i=1;i<=ksum;i++) { for(ll j=L[i];j<=R[i];j++) { pos[j]=i; } } } ll C(ll n,ll m,ll p) { return (n>=m&&n>=0&&m>=0)?(jc[n]*jc_inv[m]%p)*jc_inv[n-m]%p:0; } void addl(ll l,ll r,ll &sum) { sum=((sum+C(l,r,p))%p)*inv[2]%p; } void addr(ll l,ll r,ll &sum) { sum=(sum+C(l,r,p))%p; } void dell(ll l,ll r,ll &sum) { sum=(sum*2%p-C(l,r,p)+p)%p; } void delr(ll l,ll r,ll &sum) { sum=(sum-C(l,r,p)+p)%p; } int main() { ll n=100000,m,l=1,r=0,sum=1,i; cin>>m; inv[1]=1; jc[0]=jc_inv[0]=jc[1]=jc_inv[1]=1; for(i=2;i<=n;i++) { inv[i]=(p-p/i)*inv[p%i]%p; jc[i]=jc[i-1]*i%p; jc_inv[i]=jc_inv[i-1]*inv[i]%p; } init(n,m); for(i=1;i<=m;i++) { cin>>q[i].l>>q[i].r; q[i].id=i; } sort(q+1,q+1+m,q_cmp); for(i=1;i<=m;i++) { while(l>q[i].l) { l--; addl(l,r,sum); } while(r<q[i].r) { r++; addr(l,r,sum); } while(l<q[i].l) { dell(l,r,sum); l++; } while(r>q[i].r) { delr(l,r,sum); r--; } ans[q[i].id]=sum; } for(i=1;i<=m;i++) { cout<<ans[i]<<endl; } return 0; }
luogu P4137 Rmq Problem / mex
-
像 luogu P3709 大爷的字符串题 这样再次记录此时答案的来源复杂度会爆炸(也可能是我写法的问题)。
点击查看暴力代码
int a[200010],cnt[200010],ans[200010],L[200010],R[200010],pos[200010],klen,ksum; struct ask { int l,r,id; }q[200010]; bool q_cmp(ask a,ask b) { return (pos[a.l]==pos[b.l])?((pos[a.l]%2==1)?(a.r<b.r):(a.r>b.r)):(a.l<b.l); } void init(int n,int m) { klen=n/sqrt(m)+1; ksum=n/klen; for(int i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(int i=1;i<=ksum;i++) { for(int j=L[i];j<=R[i];j++) { pos[j]=i; } } } void add(int x,int &sum) { cnt[a[x]]++; if(cnt[a[x]]==1&&sum==a[x]) { while(cnt[sum]>=1) { sum++; } } } void del(int x,int &sum) { cnt[a[x]]--; sum=(cnt[a[x]]==0)?min(sum,a[x]):sum; } int main() { int n,m,l=1,r=0,sum=0,i; cin>>n>>m; for(i=1;i<=n;i++) { cin>>a[i]; } init(n,m); for(i=1;i<=m;i++) { cin>>q[i].l>>q[i].r; q[i].id=i; } sort(q+1,q+1+m,q_cmp); for(i=1;i<=m;i++) { while(l>q[i].l) { l--; add(l,sum); } while(r<q[i].r) { r++; add(r,sum); } while(l<q[i].l) { del(l,sum); l++; } while(r>q[i].r) { del(r,sum); r--; } ans[q[i].id]=sum; } for(i=1;i<=m;i++) { cout<<ans[i]<<endl; } return 0; }
-
只减不加回滚莫队板子。
- 考虑只进行一个操作,剩下的交给回滚解决。
- 先暴力统计出区间初始的答案
- 仍对询问进行分块,排序方式为以 \(l\) 所在的块的编号为第一关键字升序排序,以 \(r\) 为第二关键字降序排序。
- 按顺序处理询问。
- 若当前询问的左右端点所属的块相同,则暴力统计答案,回答询问,并及时消除对临时数组的影响。
- 若当前询问的左右端点所属的块不同,
- 若当前询问左端点所属块 \(block\) 和上一个左右端点所属的块不同的询问的左端点所属块 \(lastblock\) 不同,则将莫队区间的 \(l\) 指针初始化为 \(block\) 的左端点, \(r\) 指针的右端点初始化为 \(n\),使其变成一个极大区间。
- 若 \(r\) 指针大于当前询问的右端点,则向左不断扩展 \(r\) 指针,直至 \(r\) 等于当前询问的右端点。
- 若 \(l\) 指针小于当前询问的左端点,则向右不断扩展 \(l\) 指针,直至 \(l\) 等于当前询问的左端点。
- 在回答当前询问后撤销左端点的移动,使 \(l\) 指针回滚到 \(block\) 的左端点。
- 即对于左右端点不在一个块的询问, \(l\) 始终等于 \(block\) 的左端点。
- 一些注意事项
- 暴力统计答案和回滚操作时,均要使用额外的临时变量,以避免对下次询问产生不必要的影响。
- 初始化 \(l\) 和 \(r\) 指针必须对于于左右端点不在一个块的询问,一共至少要进行一次,仅用当前询问的左端点所在的块和上一个询问的左端点进行比较是错误的。
- 块长取 \(\frac{n}{\sqrt{m}}\) 。
点击查看正解
int a[200010],cnt[200010],lscnt[200010],ans[200010],L[200010],R[200010],pos[200010],klen,ksum; struct ask { int l,r,id; }q[200010]; bool q_cmp(ask a,ask b) { return (pos[a.l]==pos[b.l])?(a.r>b.r):(a.l<b.l); } void init(int n,int m) { klen=n/sqrt(m)+1; ksum=n/klen; for(int i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(int i=1;i<=ksum;i++) { for(int j=L[i];j<=R[i];j++) { pos[j]=i; } } } void add(int cnt[],int x) { cnt[a[x]]++; } void del(int cnt[],int x,int &sum) { cnt[a[x]]--; sum=(cnt[a[x]]==0)?min(sum,a[x]):sum; } int main() { int n,m,l=1,r,anss=0,sum=0,tmp=0,lastblock=0,i,j; cin>>n>>m; r=n; for(i=1;i<=n;i++) { cin>>a[i]; } init(n,m); for(i=1;i<=m;i++) { cin>>q[i].l>>q[i].r; q[i].id=i; } sort(q+1,q+1+m,q_cmp); for(i=1;i<=n;i++) { add(cnt,i);//加入原序列 } while(cnt[anss]>=1) { anss++; } for(i=1;i<=m;i++) { if(pos[q[i].l]==pos[q[i].r]) { sum=0; for(j=q[i].l;j<=q[i].r;j++) { add(lscnt,j); } while(lscnt[sum]>=1) { sum++; } for(j=q[i].l;j<=q[i].r;j++) { lscnt[a[j]]--; } } else { if(lastblock!=pos[q[i].l]) { while(r<n) { r++; add(cnt,r); } while(l<L[pos[q[i].l]]) { del(cnt,l,anss);//更新原序列答案 l++; } tmp=anss; lastblock=pos[q[i].l]; } while(r>q[i].r) { del(cnt,r,tmp); r--; } sum=tmp; while(l<q[i].l) { del(cnt,l,sum); l++; } while(l>L[pos[q[i].l]])//回滚 { l--; add(cnt,l); } } ans[q[i].id]=sum; } for(i=1;i<=m;i++) { cout<<ans[i]<<endl; } return 0; }
luogu P5906 【模板】回滚莫队&不删除莫队
-
记录每个数的最早出现位置和最晚出现位置,然后回滚莫队维护即可。
点击查看代码
int a[200010],b[200010],pos1[200010],lspos1[200010],pos2[200010],lspos2[200010],ans[200010],L[200010],R[200010],pos[200010],klen,ksum; struct ask { int l,r,id; }q[200010]; bool q_cmp(ask a,ask b) { return (pos[a.l]==pos[b.l])?(a.r<b.r):(a.l<b.l); } void init(int n,int m) { klen=n/sqrt(m)+1; ksum=n/klen; for(int i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(int i=1;i<=ksum;i++) { for(int j=L[i];j<=R[i];j++) { pos[j]=i; } } } void add1(int pos1[],int pos2[],int x,int &sum) { if(pos1[a[x]]==0) { pos1[a[x]]=pos2[a[x]]=x;//此时位置相减得 0 ,取 max 无意义 } else { pos2[a[x]]=x; sum=max(sum,pos2[a[x]]-pos1[a[x]]); } } void add2(int pos1[],int pos2[],int x,int &sum) { if(pos2[a[x]]==0) { pos2[a[x]]=x; } else { sum=max(sum,pos2[a[x]]-x); } } int main() { int n,m,l=1,r=0,sum=0,tmp=0,lastblock=0,i,j; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; b[i]=a[i]; } sort(b+1,b+1+n); b[0]=unique(b+1,b+1+n)-(b+1); for(i=1;i<=n;i++) { a[i]=lower_bound(b+1,b+1+b[0],a[i])-b; } cin>>m; init(n,m); for(i=1;i<=m;i++) { cin>>q[i].l>>q[i].r; q[i].id=i; } sort(q+1,q+1+m,q_cmp); for(i=1;i<=m;i++) { if(pos[q[i].l]==pos[q[i].r]) { sum=0; for(j=q[i].l;j<=q[i].r;j++) { add1(lspos1,lspos2,j,sum); } for(j=q[i].l;j<=q[i].r;j++) { lspos1[a[j]]=lspos2[a[j]]=0; } } else { if(lastblock!=pos[q[i].l]) { memset(pos1,0,sizeof(pos1)); memset(pos2,0,sizeof(pos2)); l=R[pos[q[i].l]]+1; r=R[pos[q[i].l]]; tmp=0; lastblock=pos[q[i].l]; } while(r<q[i].r) { r++; add1(pos1,pos2,r,tmp); } sum=tmp; while(l>q[i].l) { l--; add2(pos1,pos2,l,sum);//以免造成影响 } while(l<R[pos[q[i].l]]+1) { pos2[a[l]]=(pos1[a[l]]==0)?0:max(pos1[a[l]],pos2[a[l]]);//回顾时消除影响 l++; } } ans[q[i].id]=sum; } for(i=1;i<=m;i++) { cout<<ans[i]<<endl; } return 0; }
4.25
闲话
- 详见 2024 HE中考 游记 4.25 。
做题纪要
4.26
闲话
- 详见 2024 HE中考 游记 4.26 。
- 信息课下了个
Dev-C++
。 - 下午到机房后发现 @K8He 和 @jijidawang 又走了。
- 由于下午上信息课占了奥赛,所以第 \(10\) 节和晚一改奥赛了。晚新闻找现班主任交涉了下,让晚新闻也改奥赛了(貌似其他奥赛是直接改的,就信奥还回去交涉了一下)。
- 晚上到机房后发现空调没开了,机房众人在一起商讨了下如何打开空调后成功将其打开,并把门关上了。然后 \(huge\) 进来后很费解,问我们不热吗,我们说热,他说热还不把门开开,我们说开着空调就没开门,但空调没多大用,他说空调坏了,“你们现在先静下来,心静自然凉,要是感觉热说明你们太浮躁了”。
做题纪要
SP20644 ZQUERY - Zero Query
-
由 \(\sum\limits_{i=l}^{r}a_{i}=\sum\limits_{i=1}^{r}a_{i}-\sum\limits_{i=1}^{l-1}a_{i}\) ,题目等价于在 \([l-1,r]\) 中相同的 \(\sum\) 的最远间隔距离。
-
由于前缀和后可能会出现负数,可以加上一个较大的数或直接离散化。
点击查看代码
int a[50010],b[50010],pos1[50010],lspos1[50010],pos2[50010],lspos2[50010],ans[50010],L[50010],R[50010],pos[50010],klen,ksum; struct ask { int l,r,id; }q[50010]; bool q_cmp(ask a,ask b) { return (pos[a.l]==pos[b.l])?(a.r<b.r):(a.l<b.l); } void init(int n,int m) { klen=n/sqrt(m)+1; ksum=n/klen; for(int i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(int i=1;i<=ksum;i++) { for(int j=L[i];j<=R[i];j++) { pos[j]=i; } } } void add1(int pos1[],int pos2[],int x,int &sum) { if(pos1[a[x]]==0) { pos1[a[x]]=pos2[a[x]]=x; } else { pos2[a[x]]=x; sum=max(sum,pos2[a[x]]-pos1[a[x]]); } } void add2(int pos1[],int pos2[],int x,int &sum) { if(pos2[a[x]]==0) { pos2[a[x]]=x; } else { sum=max(sum,pos2[a[x]]-x); } } int main() { int n,m,l=1,r=0,sum=0,tmp=0,lastblock=0,i,j; cin>>n>>m; n++; for(i=2;i<=n;i++) { cin>>a[i]; a[i]+=a[i-1]; } for(i=1;i<=n;i++) { b[i]=a[i]; } cout<<endl; sort(b+1,b+1+n); b[0]=unique(b+1,b+1+n)-(b+1); for(i=1;i<=n;i++) { a[i]=lower_bound(b+1,b+1+b[0],a[i])-b; } init(n,m); for(i=1;i<=m;i++) { cin>>q[i].l>>q[i].r; q[i].r++; q[i].id=i; } sort(q+1,q+1+m,q_cmp); for(i=1;i<=m;i++) { if(pos[q[i].l]==pos[q[i].r]) { sum=0; for(j=q[i].l;j<=q[i].r;j++) { add1(lspos1,lspos2,j,sum); } for(j=q[i].l;j<=q[i].r;j++) { lspos1[a[j]]=lspos2[a[j]]=0; } } else { if(lastblock!=pos[q[i].l]) { memset(pos1,0,sizeof(pos1)); memset(pos2,0,sizeof(pos2)); l=R[pos[q[i].l]]+1; r=R[pos[q[i].l]]; tmp=0; lastblock=pos[q[i].l]; } while(r<q[i].r) { r++; add1(pos1,pos2,r,tmp); } sum=tmp; while(l>q[i].l) { l--; add2(pos1,pos2,l,sum); } while(l<R[pos[q[i].l]]+1) { pos2[a[l]]=max(pos1[a[l]],pos2[a[l]]); l++; } } ans[q[i].id]=sum; } for(i=1;i<=m;i++) { cout<<ans[i]<<endl; } return 0; }
CF940F Machine Learning
-
离散化后带修莫队处理出现次数,暴力求 \(mex\) 即可。
- 为什么暴力求 \(mex\) 是可做的?
- 对于答案 \(ans\) ,一定有 \(\sum\limits_{i=1}^{ans-1}i=\frac{ans(ans-1)}{2} \le n+m\) ,则 \(ans\) 的正整数解满足 \(ans \approx \sqrt{n}\) ,则查询复杂度为 \(O(m\sqrt{n})\) 。
点击查看代码
int a[200010],b[200010],num[200010],cnt[200010],L[200010],R[200010],pos[200010],ans[200010],klen,ksum; struct ask { int l,r,id,tim; }q[200010]; struct change { int pos,col; }c[200010]; bool q_cmp(ask a,ask b) { return (pos[a.l]==pos[b.l])?((pos[a.r]==pos[b.r])?(a.tim<b.tim):(a.r<b.r)):(a.l<b.l); } void init(int n) { klen=pow(n,2.0/3); ksum=n/klen; for(int i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(int i=1;i<=ksum;i++) { for(int j=L[i];j<=R[i];j++) { pos[j]=i; } } } void add(int x) { num[cnt[x]]--; cnt[x]++; num[cnt[x]]++; } void del(int x) { num[cnt[x]]--; cnt[x]--; num[cnt[x]]++; } int main() { int n,m,mm=0,pd,l=1,r=0,tim=0,qcnt=0,ccnt=0,i; cin>>n>>m; mm=n; for(i=1;i<=n;i++) { cin>>a[i]; b[i]=a[i]; } init(n); for(i=1;i<=m;i++) { cin>>pd; if(pd==1) { qcnt++; cin>>q[qcnt].l>>q[qcnt].r; q[qcnt].id=qcnt; q[qcnt].tim=ccnt; } else { ccnt++; cin>>c[ccnt].pos>>c[ccnt].col; mm++; b[mm]=c[ccnt].col; } } sort(b+1,b+1+mm); b[0]=unique(b+1,b+1+mm)-(b+1); for(i=1;i<=n;i++) { a[i]=lower_bound(b+1,b+1+b[0],a[i])-b; } for(i=n+1;i<=mm;i++) { c[i-n].col=lower_bound(b+1,b+1+b[0],c[i-n].col)-b; } sort(q+1,q+1+qcnt,q_cmp); for(i=1;i<=qcnt;i++) { while(l>q[i].l) { l--; add(a[l]); } while(r<q[i].r) { r++; add(a[r]); } while(l<q[i].l) { del(a[l]); l++; } while(r>q[i].r) { del(a[r]); r--; } while(tim<q[i].tim) { tim++; if(l<=c[tim].pos&&c[tim].pos<=r) { del(a[c[tim].pos]); add(c[tim].col); } swap(a[c[tim].pos],c[tim].col); } while(tim>q[i].tim) { if(l<=c[tim].pos&&c[tim].pos<=r) { del(a[c[tim].pos]); add(c[tim].col); } swap(a[c[tim].pos],c[tim].col); tim--; } ans[q[i].id]=1; while(num[ans[q[i].id]]>=1) { ans[q[i].id]++; } } for(i=1;i<=qcnt;i++) { cout<<ans[i]<<endl; } return 0; }
- 为什么暴力求 \(mex\) 是可做的?
BZOJ4358 permu
-
建一棵权值线段树,每次插入时将其变成 \(1\) ,删除时将其变成 \(0\) ,然后线段树维护最长连续 \(1\) 的个数即可。
-
注意代码与 SP1716 GSS3 - Can you answer these queries III | luogu P4513 小白逛公园 略有不同。
点击查看代码
int a[200010],ans[200010],L[200010],R[200010],pos[200010],klen,ksum; struct ask { int l,r,id; }q[200010]; bool q_cmp(ask a,ask b) { return (pos[a.l]==pos[b.l])?((pos[a.l]%2==1)?(a.r<b.r):(a.r>b.r)):(a.l<b.l); } struct SegmentTree { int l,r,maxl,maxr,ans; }tree[200010]; int lson(int x) { return x*2; } int rson(int x) { return x*2+1; } void pushup(int rt) { tree[rt].maxl=tree[lson(rt)].maxl+(tree[lson(rt)].maxl==tree[lson(rt)].r-tree[lson(rt)].l+1)*tree[rson(rt)].maxl; tree[rt].maxr=tree[rson(rt)].maxr+(tree[rson(rt)].maxr==tree[rson(rt)].r-tree[rson(rt)].l+1)*tree[lson(rt)].maxr; tree[rt].ans=max(max(tree[lson(rt)].ans,tree[rson(rt)].ans),tree[lson(rt)].maxr+tree[rson(rt)].maxl); } void build(int rt,int l,int r) { tree[rt].l=l; tree[rt].r=r; if(l==r) { return; } int mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void update(int rt,int pos,int ans) { if(tree[rt].l==tree[rt].r) { tree[rt].ans=tree[rt].maxl=tree[rt].maxr=ans; return; } else { int mid=(tree[rt].l+tree[rt].r)/2; if(mid>=pos) { update(lson(rt),pos,ans); } else { update(rson(rt),pos,ans); } pushup(rt); } } SegmentTree query(int rt,int l,int r) { if(l<=tree[rt].l&&tree[rt].r<=r) { return tree[rt]; } else { int mid=(tree[rt].l+tree[rt].r)/2; if(r<=mid) { return query(lson(rt),l,r); } else { if(l>mid) { return query(rson(rt),l,r); } else { SegmentTree p=query(lson(rt),l,r),q=query(rson(rt),l,r),num; num.maxl=p.maxl+(p.maxl==p.r-p.l+1)*q.maxl; num.maxr=q.maxr+(q.maxr==q.r-q.l+1)*p.maxr; num.ans=max(max(p.ans,q.ans),p.maxr+q.maxl); return num; } } } } void init(int n,int m) { klen=n/sqrt(m)+1; ksum=n/klen; for(int i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(int i=1;i<=ksum;i++) { for(int j=L[i];j<=R[i];j++) { pos[j]=i; } } } void add(int x) { update(1,a[x],1); } void del(int x) { update(1,a[x],0); } int main() { int n,m,l=1,r=0,i; cin>>n>>m; for(i=1;i<=n;i++) { cin>>a[i]; } build(1,1,n); init(n,m); for(i=1;i<=m;i++) { cin>>q[i].l>>q[i].r; q[i].id=i; } sort(q+1,q+1+m,q_cmp); for(i=1;i<=m;i++) { while(l>q[i].l) { l--; add(l); } while(r<q[i].r) { r++; add(r); } while(l<q[i].l) { del(l); l++; } while(r>q[i].r) { del(r); r--; } ans[q[i].id]=query(1,1,n).ans; } for(i=1;i<=m;i++) { cout<<ans[i]<<endl; } return 0; }
4.27
闲话
- 早操时候,德育主任公布了下学校体育中考的成绩,满分率 \(50 \%\) 以上,优秀( \(27.5pts\) 及以上)率 \(90 \%\) 以上,这下垫底了。
- 年级主任遵守了期中总结大会的约定,中考体育之后停掉了课间操,早操的速度也降下来了,但要注重整齐度。
- 因今天晚自习上的是周日的晚自习,所以体活没了,改成了周日的公自。到点的时候就被 \(miaomiao\) 说现班主任让回去,说不知道说什么事。先去四楼去了趟厕所,遇见了原班主任和一个不知道什么老师,那老师问我们干啥的,原班主任说“他们来上厕所,他们是上奥赛的”。到教室后,现班主任说了些杂七杂八的东西,包括但不限于体育课停上,估计是改成理化实验;课间操停了;因一模前不能太放松,故取消今天的体活;鉴于上两次考试的平均分太高,所以这次决定停掉考后讲评,考完试到放假前全部上奥赛;五一就放不到 \(48h\) ;因没有宿舍内务和违纪扣分,成功获得量化第二,但本应该是第一的,让我们以后都保持第一;问了问我们体育中考成绩。
- 晚三突然被通知 \(21:20\) 下课,去上“小体活”。直接去机房了,看见 @xrlong 和 @5k_sync_closer 在 【数据删除】 ,然后 \(feifei\) 进来转了一圈,但没管。
做题纪要
牛客练习赛124_PLUS A 智乃的gcd构造
-
假设 \(a<b\) ,则限制条件转化为 \(\begin{cases} a+b \ge y \\ b-a \ge x \end{cases}\) ,又因为 \(x,y,z\) 的值域与 \(a,b\) 的值域相差很大,不难得到当 \(a\) 特别小,但 \(b\) 特别大时一定满足条件 ,故一组构造方案为 \(\begin{cases} a=z \\ b=5 \times 10^{18}-5 \times 10^{18} \mod z \end{cases}\) ,此时有 \(\begin{cases} 1 \le a \le 1 \times 10^{18} \\ 4 \times 10^{18} \le b \le 5 \times 10^{18} \end{cases}\) 。
点击查看代码
int main() { ll x,y,z,maxx=5e18; cin>>x>>y>>z; cout<<z<<" "<<maxx-maxx%z<<endl; return 0; }
luogu P2336 [SCOI2012] 喵星球上的点名
-
将所有喵星人的姓与名拼起来,用分割符隔开。
-
二分处理出询问在 \(sa\) 数组上的左右端点。第一问转化成了求区间不同元素个数,莫队维护即可;第二问转化成了求元素出现次数,在插入时将所有可能出现的次数加上,在删除时将所有不可能出现的次数减去,建议画图理解。
点击查看代码
int s[300010],sa[300010],rk[600010],oldrk[600010],id[300010],sa_cnt[300010],key[300010],a[300010],ans1[300010],ans2[300010],cnt[300010],L[300010],R[300010],pos[300010],klen,ksum; struct ask { int l,r,id; }q[300010]; bool q_cmp(ask a,ask b) { return (pos[a.l]==pos[b.l])?((pos[a.l]%2==1)?(a.r<b.r):(a.r>b.r)):(a.l<b.l); } int val(int x) { return (int)x; } void counting_sort(int n,int m) { memset(sa_cnt,0,sizeof(sa_cnt)); for(int i=1;i<=n;i++) { sa_cnt[key[i]]++; } for(int i=1;i<=m;i++) { sa_cnt[i]+=sa_cnt[i-1]; } for(int i=n;i>=1;i--) { sa[sa_cnt[key[i]]]=id[i]; sa_cnt[key[i]]--; } } void init_sa(int s[],int len) { int m=300000,tot=0,num=0,i,w; for(i=1;i<=len;i++) { rk[i]=val(s[i]); id[i]=i; key[i]=rk[id[i]]; } counting_sort(len,m); for(w=1;tot!=len;w<<=1,m=tot) { num=0; for(i=len;i>=len-w+1;i--) { num++; id[num]=i; } for(i=1;i<=len;i++) { if(sa[i]>w) { num++; id[num]=sa[i]-w; } } for(i=1;i<=len;i++) { key[i]=rk[id[i]]; } counting_sort(len,m); for(i=1;i<=len;i++) { oldrk[i]=rk[i]; } tot=0; for(i=1;i<=len;i++) { tot+=(oldrk[sa[i]]!=oldrk[sa[i-1]]||oldrk[sa[i]+w]!=oldrk[sa[i-1]+w]); rk[sa[i]]=tot; } } } void init(int n,int m) { klen=n/sqrt(m)+1; ksum=n/klen; for(int i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(int i=1;i<=ksum;i++) { for(int j=L[i];j<=R[i];j++) { pos[j]=i; } } } void add(int x,int &sum,int i,int m) { cnt[a[x]]++; sum+=(cnt[a[x]]==1); ans2[a[x]]+=(cnt[a[x]]==1)*(m-i+1); } void del(int x,int &sum,int i,int m) { cnt[a[x]]--; sum-=(cnt[a[x]]==0); ans2[a[x]]-=(cnt[a[x]]==0)*(m-i+1); } bool check1(int mid,int j,int pd) { return s[sa[mid]+j-1]<pd; } bool check2(int mid,int j,int pd) { return s[sa[mid]+j-1]>pd; } int main() { int n,m,len=0,maxx=10000,le,pd,l,r,mid,sum=0,i,j,k; cin>>n>>m; for(i=1;i<=n;i++) { for(k=1;k<=2;k++) { cin>>le; for(j=1;j<=le;j++) { len++; cin>>s[len]; a[len]=i; } len++; maxx++; s[len]=maxx; } } init_sa(s,len); init(len,m); for(i=1;i<=m;i++) { cin>>le; q[i].l=1; q[i].r=len; q[i].id=i; for(j=1;j<=le;j++) { cin>>pd; l=q[i].l; r=q[i].r; while(l<=r) { mid=(l+r)/2; if(check1(mid,j,pd)==true) { q[i].l=l=mid+1; } else { r=mid-1; } } l=q[i].l; r=q[i].r; while(l<=r) { mid=(l+r)/2; if(check2(mid,j,pd)==true) { q[i].r=r=mid-1; } else { l=mid+1; } } } } sort(q+1,q+1+m,q_cmp); l=1; r=0; for(i=1;i<=m;i++) { while(l>q[i].l) { l--; add(sa[l],sum,i,m); } while(r<q[i].r) { r++; add(sa[r],sum,i,m); } while(l<q[i].l) { del(sa[l],sum,i,m); l++; } while(r>q[i].r) { del(sa[r],sum,i,m); r--; } ans1[q[i].id]=sum; } for(i=1;i<=m;i++) { cout<<ans1[i]<<endl; } for(i=1;i<=n;i++) { cout<<ans2[i]<<" "; } return 0; }
[ABC351A] The bottom of the ninth
-
不等式移项后,有 \(\sum\limits_{i=1}^{9}a_{i}-\sum\limits_{i=1}^{8}b_{i}+1\) 即为所求。
点击查看代码
int main() { int ans=1,a,i; for(i=1;i<=9;i++) { cin>>a; ans+=a; } for(i=1;i<=8;i++) { cin>>a; ans-=a; } cout<<ans<<endl; return 0; }
[ABC351B] Spot the Difference
-
模拟即可。
点击查看代码
char a[120][120],b[120][120]; int main() { int n,i,j; cin>>n; for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { cin>>a[i][j]; } } for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { cin>>b[i][j]; if(a[i][j]!=b[i][j]) { cout<<i<<" "<<j<<endl; return 0; } } } }
4.28
闲话
- 早上有体活,但第 \(1\) 节课是奥赛,吃完饭就直接来机房了。但 @hs_mo , @HANGRY_sol , @wang54321 直到 \(7:45\) 才到机房,他们去上“物理奥赛”了。
做题纪要
[ABC351C] Merge the balls
-
每次都需加入第 \(i\) 个球再判断不是很可做,考虑先判断再将其加进去,开个栈维护即可。
点击查看代码
int a[200010]; stack<int>s; int main() { int n,i; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; while(s.empty()==0&&s.top()==a[i]) { a[i]++; s.pop(); } s.push(a[i]); } cout<<s.size()<<endl; return 0; }
[ABC351F] Double Sum
-
有 \(\begin{aligned} \sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}\max(a_{j}-a_{i},0) &=\sum\limits_{j=1}^{n}\sum\limits_{i=1}^{j-1}\max(a_{j}-a_{i},0) \\ &=\sum\limits_{j=1}^{n}(\sum\limits_{i=1}^{j-1}[a_{i} \le a_{j}] \times a_{j})-(\sum\limits_{i=1}^{j-1}[a_{i} \le a_{j}] \times a_{i}) \end{aligned}\) ,离散化后权值树状数组维护即可。
点击查看代码
ll a[400010],b[400010],d[400010],c[2][400010]; ll lowbit(ll x) { return (x&(-x)); } void add(ll n,ll x,ll key,ll c[]) { for(ll i=x;i<=n;i+=lowbit(i)) { c[i]+=key; } } ll getsum(ll x,ll c[]) { ll ans=0; for(ll i=x;i>=1;i-=lowbit(i)) { ans+=c[i]; } return ans; } int main() { ll n,ans=0,ls,i; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; b[i]=a[i]; } sort(b+1,b+1+n); b[0]=unique(b+1,b+1+n)-(b+1); for(i=1;i<=n;i++) { ls=lower_bound(b+1,b+1+b[0],a[i])-b; d[ls]=a[i]; a[i]=ls; } for(i=1;i<=n;i++) { ans+=d[a[i]]*getsum(a[i],c[0])-getsum(a[i],c[1]); add(n,a[i],1,c[0]); add(n,a[i],d[a[i]],c[1]); } cout<<ans<<endl; return 0; }
[ABC351D] Grid and Magnet
-
注意
#
四周的边界,然后 \(BFS\) 处理即可。点击查看代码
int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1},vis[1010][1010]; char c[1010][1010]; void bfs(int x,int y,int n,int m,int &sum,int &ans) { queue<pair<int,int> >q,v; sum++; vis[x][y]=1; q.push(make_pair(x,y)); while(q.empty()==0) { int flag=0,nx=q.front().first,ny=q.front().second; q.pop(); for(int i=0;i<=3;i++) { if(1<=nx+dx[i]&&nx+dx[i]<=n&&1<=ny+dy[i]&&ny+dy[i]<=m) { if(c[nx+dx[i]][ny+dy[i]]=='#') { flag=1; v.push(make_pair(nx,ny));//边界处理 break; } } } if(flag==0) { for(int i=0;i<=3;i++) { if(1<=nx+dx[i]&&nx+dx[i]<=n&&1<=ny+dy[i]&&ny+dy[i]<=m&&c[nx+dx[i]][ny+dy[i]]=='.'&&vis[nx+dx[i]][ny+dy[i]]==0) { sum++; vis[nx+dx[i]][ny+dy[i]]=1; q.push(make_pair(nx+dx[i],ny+dy[i])); } } } } ans=max(ans,sum); while(v.empty()==0) { int nx=v.front().first,ny=v.front().second; vis[nx][ny]=0; v.pop(); } } int main() { int n,m,sum=0,ans=0,i,j; cin>>n>>m; for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { cin>>c[i][j]; } } for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { if(c[i][j]=='.'&&vis[i][j]==0) { sum=0; bfs(i,j,n,m,sum,ans); } } } cout<<ans<<endl; return 0; }
luogu P1138 第 k 小整数
-
权值线段树板子。
- 用线段树对值域维护桶即可。
点击查看代码
int a[120010]; struct SegmentTree { int l,r,sum; }tree[120010]; int lson(int x) { return x*2; } int rson(int x) { return x*2+1; } void pushup(int rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; } void build(int rt,int l,int r) { tree[rt].l=l; tree[rt].r=r; if(l==r) { tree[rt].sum=0; return; } int mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void update(int rt,int pos,int val) { if(tree[rt].l==tree[rt].r) { tree[rt].sum+=val; return; } else { int mid=(tree[rt].l+tree[rt].r)/2; if(pos<=mid) { update(lson(rt),pos,val); } else { update(rson(rt),pos,val); } pushup(rt); } } int kth_max(int rt,int k) { if(tree[rt].l==tree[rt].r) { return tree[rt].l; } else { if(k<=tree[rson(rt)].sum) { return kth_max(rson(rt),k);//都在右子树 } else { return kth_max(lson(rt),k-tree[rson(rt)].sum); } } } int kth_min(int rt,int k) { if(tree[rt].l==tree[rt].r) { return tree[rt].l; } else { if(k<=tree[lson(rt)].sum) { return kth_min(lson(rt),k);//都在左子树 } else { return kth_min(rson(rt),k-tree[lson(rt)].sum); } } } int main() { int n,k,i; cin>>n>>k; for(i=1;i<=n;i++) { cin>>a[i]; } sort(a+1,a+1+n); a[0]=unique(a+1,a+1+n)-(a+1); if(a[0]<k) { cout<<"NO RESULT"<<endl; } else { build(1,1,a[a[0]]); for(i=1;i<=a[0];i++) { update(1,a[i],1); } cout<<kth_min(1,k)<<endl;//和下面的写法是等价的 //cout<<kth_max(1,a[0]-k+1)<<endl; } return 0; }
luogu P3374 【模板】 树状数组 1
-
单点修改,区间查询板子。
- 可用动态开点线段树维护。
- 在最初只建立一个根节点,代表整个区间。当需要访问线段树的某棵子树(某个子区间)时,再建立代表这个子区间的节点。
- 需要用变量记录左右儿子的编号。
- 空间复杂度为 \(O(2n)\) 。
点击查看代码
int a[1000010],rt_sum=0; struct SegmentTree { int ls,rs,sum; }tree[1000010]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) void pushup(int rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; } int build() { rt_sum++; lson(rt_sum)=rson(rt_sum)=tree[rt_sum].sum=0; return rt_sum; } void update(int rt,int l,int r,int pos,int val) { if(l==r) { tree[rt].sum+=val; return; } int mid=(l+r)/2; if(pos<=mid) { lson(rt)=(lson(rt)==0)?build():lson(rt);//新建节点 update(lson(rt),l,mid,pos,val); } else { rson(rt)=(rson(rt)==0)?build():rson(rt); update(rson(rt),mid+1,r,pos,val); } pushup(rt); } int query(int rt,int l,int r,int x,int y) { if(rt==0)//查询时不需要新建节点 { return 0; } if(x<=l&&r<=y) { return tree[rt].sum; } int mid=(l+r)/2,ans=0; if(x<=mid) { ans+=query(lson(rt),l,mid,x,y); } if(y>mid) { ans+=query(rson(rt),mid+1,r,x,y); } return ans; } int main() { int n,m,l,r,val,root=0,pd,i; cin>>n>>m; root=build(); for(i=1;i<=n;i++) { cin>>a[i]; update(root,1,n,i,a[i]); } for(i=1;i<=m;i++) { cin>>pd>>l>>r; if(pd==1) { update(root,1,n,l,r); } if(pd==2) { cout<<query(root,1,n,l,r)<<endl; } } return 0; }
- 可用动态开点线段树维护。
4.29
闲话
- 详见 2024 HE中考 游记 4.29 。
做题纪要
luogu P3372 【模板】 线段树 1
-
区间修改,区间查询板子。
- 下传懒惰标记时注意不要新建节点,否则空间复杂度不保真。
点击查看代码
ll a[200010],rt_sum=0; struct SegmentTree { ll ls,rs,sum,lazy; }tree[200010]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) void pushup(ll rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; } ll build() { rt_sum++; lson(rt_sum)=rson(rt_sum)=tree[rt_sum].sum=tree[rt_sum].lazy=0; return rt_sum; } void pushdown(ll rt,ll l,ll r) { if(tree[rt].lazy!=0) { ll mid=(l+r)/2; if(lson(rt)!=0) { tree[lson(rt)].lazy+=tree[rt].lazy; tree[lson(rt)].sum+=tree[rt].lazy*(mid-l+1); } if(rson(rt)!=0) { tree[rson(rt)].lazy+=tree[rt].lazy; tree[rson(rt)].sum+=tree[rt].lazy*(r-(mid+1)+1); } tree[rt].lazy=0; } } void update(ll rt,ll l,ll r,ll x,ll y,ll val) { if(x<=l&&r<=y) { tree[rt].lazy+=val; tree[rt].sum+=val*(r-l+1); return; } pushdown(rt,l,r); ll mid=(l+r)/2,ans=0; if(x<=mid) { lson(rt)=(lson(rt)==0)?build():lson(rt); update(lson(rt),l,mid,x,y,val); } if(y>mid) { rson(rt)=(rson(rt)==0)?build():rson(rt); update(rson(rt),mid+1,r,x,y,val); } pushup(rt); } ll query(ll rt,ll l,ll r,ll x,ll y) { if(rt==0) { return 0; } if(x<=l&&r<=y) { return tree[rt].sum; } pushdown(rt,l,r); ll mid=(l+r)/2,ans=0; if(x<=mid) { ans+=query(lson(rt),l,mid,x,y); } if(y>mid) { ans+=query(rson(rt),mid+1,r,x,y); } return ans; } int main() { ll n,m,pd,l,r,val,root=0,i; cin>>n>>m; root=build(); for(i=1;i<=n;i++) { cin>>a[i]; update(root,1,n,i,i,a[i]); } for(i=1;i<=m;i++) { cin>>pd; if(pd==1) { cin>>l>>r>>val; update(root,1,n,l,r,val); } if(pd==2) { cin>>l>>r; cout<<query(root,1,n,l,r)<<endl; } } return 0; }
luogu P1637 三元上升子序列
-
多倍经验: CF61E Enemy is weak
-
有 \(\begin{aligned} \sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}\sum\limits_{k=j+1}^{n}[a_{i}<a_{j}<a_{k}] &=\sum\limits_{j=1}^{n}(\sum\limits_{i=1}^{j-1}[a_{i}<a_{j}]) \times (\sum\limits_{k=j+1}^{n}[a_{j}<a_{k}]) \\ &=\sum\limits_{j=1}^{n}(\sum\limits_{i=1}^{j-1}[a_{i}<a_{j}]) \times (n-j-\sum\limits_{k=j+1}^{n}[a_{j} \le a_{k}]) \end{aligned}\) ,权值树状数组维护即可。
点击查看代码
ll c[2][100010],a[100010],l[100010],r[100010]; ll lowbit(ll x) { return (x&(-x)); } void add(ll n,ll x,ll val,ll c[]) { for(ll i=x;i<=n;i+=lowbit(i)) { c[i]+=val; } } ll getsum(ll x,ll c[]) { ll ans=0; for(ll i=x;i>=1;i-=lowbit(i)) { ans+=c[i]; } return ans; } int main() { ll n,ans=0,i; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; l[i]=getsum(a[i]-1,c[0]); add(100000,a[i],1,c[0]); } for(i=n;i>=1;i--) { r[i]=n-i-getsum(a[i],c[1]); add(100000,a[i],1,c[1]); } for(i=1;i<=n;i++) { ans+=l[i]*r[i]; } cout<<ans<<endl; return 0; }
CF1042D Petya and Array
-
记 \(sum_{i}=\sum\limits_{j=1}^{i}a_{j}\) ,则有 \(\begin{aligned} \sum\limits_{l=1}^{n}\sum\limits_{r=l}^{n}[\sum\limits_{k=l}^{r}a_{k}<t] &=\sum\limits_{l=1}^{n}\sum\limits_{r=l}^{n}[sum_{r}-sum_{l-1}<t] \\ &=\sum\limits_{l=1}^{n}\sum\limits_{r=l}^{n}[sum_{r}<t+sum_{l-1}] \\ &=\sum\limits_{r=1}^{n}\sum\limits_{l=0}^{r-1}[sum_{r}<t+sum_{l}] \\ &=\sum\limits_{r=1}^{n}\sum\limits_{l=0}^{r-1}[sum_{l}>sum_{r}-t] \\ &=\sum\limits_{r=1}^{n}(r-\sum\limits_{l=0}^{r-1}[sum_{l} \le sum_{r}-t]) \end{aligned}\) 。离散化后权值线段树维护即可。
点击查看代码
ll a[800010],sum[800010],b[800010],d[800010]; struct SegmentTree { ll l,r,sum; }tree[800010]; ll lson(ll x) { return x*2; } ll rson(ll x) { return x*2+1; } void pushup(ll rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; } void build(ll rt,ll l,ll r) { tree[rt].l=l; tree[rt].r=r; if(l==r) { tree[rt].sum=0; return; } ll mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void update(ll rt,ll pos,ll val) { if(tree[rt].l==tree[rt].r) { tree[rt].sum+=val; return; } else { ll mid=(tree[rt].l+tree[rt].r)/2; if(pos<=mid) { update(lson(rt),pos,val); } else { update(rson(rt),pos,val); } pushup(rt); } } ll query(ll rt,ll x,ll y) { if(x<=tree[rt].l&&tree[rt].r<=y) { return tree[rt].sum; } ll mid=(tree[rt].l+tree[rt].r)/2,ans=0; if(x<=mid) { ans+=query(lson(rt),x,y); } if(y>mid) { ans+=query(rson(rt),x,y); } return ans; } int main() { ll n,t,ans=0,ls=0,i; cin>>n>>t; n++; for(i=2;i<=n;i++) { cin>>a[i]; b[i]=sum[i]=sum[i-1]+a[i]; } sort(b+1,b+1+n); b[0]=unique(b+1,b+1+n)-(b+1); build(1,1,n); for(i=1;i<=n;i++) { ls=lower_bound(b+1,b+1+b[0],sum[i])-b; d[ls]=sum[i]; sum[i]=ls; } for(i=1;i<=n;i++) { ans+=i-1-query(1,1,upper_bound(b+1,b+1+b[0],d[sum[i]]-t)-b-1);//注意是 upper_bound -1 update(1,sum[i],1); } cout<<ans<<endl; return 0; }
luogu P5459 [BJOI2016] 回转寿司
-
差分处理即可。
点击查看代码
ll a[800010],sum[800010],b[800010],d[800010]; struct SegmentTree { ll l,r,sum; }tree[800010]; ll lson(ll x) { return x*2; } ll rson(ll x) { return x*2+1; } void pushup(ll rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; } void build(ll rt,ll l,ll r) { tree[rt].l=l; tree[rt].r=r; if(l==r) { tree[rt].sum=0; return; } ll mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void update(ll rt,ll pos,ll val) { if(tree[rt].l==tree[rt].r) { tree[rt].sum+=val; return; } else { ll mid=(tree[rt].l+tree[rt].r)/2; if(pos<=mid) { update(lson(rt),pos,val); } else { update(rson(rt),pos,val); } pushup(rt); } } ll query(ll rt,ll x,ll y) { if(x<=tree[rt].l&&tree[rt].r<=y) { return tree[rt].sum; } ll mid=(tree[rt].l+tree[rt].r)/2,ans=0; if(x<=mid) { ans+=query(lson(rt),x,y); } if(y>mid) { ans+=query(rson(rt),x,y); } return ans; } int main() { ll n,l,r,ans=0,ls=0,i; cin>>n>>l>>r; n++; for(i=2;i<=n;i++) { cin>>a[i]; b[i]=sum[i]=sum[i-1]+a[i]; } sort(b+1,b+1+n); b[0]=unique(b+1,b+1+n)-(b+1); build(1,1,n); for(i=1;i<=n;i++) { ls=lower_bound(b+1,b+1+b[0],sum[i])-b; d[ls]=sum[i]; sum[i]=ls; } for(i=1;i<=n;i++) { ans+=i-1-query(1,1,upper_bound(b+1,b+1+b[0],d[sum[i]]-(r+1))-b-1); ans-=i-1-query(1,1,upper_bound(b+1,b+1+b[0],d[sum[i]]-l)-b-1); update(1,sum[i],1); } cout<<ans<<endl; return 0; }
4.30
闲话
- 详见 2024 HE中考 游记 4.30 。
做题纪要
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18150601,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。