题解 P8349【[SDOI/SXOI2022] 整数序列】

· · 题解

P8349 [SDOI/SXOI2022] 整数序列 解题报告:

更好的阅读体验

题意

给定序列 a,bq 次询问两种颜色 (x,y),定义一个区间的权值为其中 a 值为 xyb 值之和,求 x 数量等于 y 数量的子区间权值最大值。

## 分析 轻视了这道题啊。 考虑根号分治,令阈值为 $S$。 小集合对小集合直接把位置归并成一个序列暴力扫一遍即可,大集合对大集合可以直接暴力+记忆化,这里的复杂度是 $O(qS+\frac{n^2}{S})$。 难点主要是小集合对大集合(令 $x$ 为小集合,$y$ 为大集合),我们发现我们将 $x$ 和 $y$ 对应位置取出来之后,对于一个 $y$ 连续段,只要其长度大到没有合法子区间能跨越它,那么它的长度就是无关紧要的了,可以缩到恰好满足条件的长度。 更具体地说,我们维护一个 $y$ 坐标集合,对于每个长度为 $x$ 的连续段,都向前找 $x+1$ 个没加入集合的 $y$ 加入集合,向后找 $x+1$ 个没加入集合的 $y$ 加入集合,那么保留集合内的 $y$ 与 $x$ 进行运算也能保证答案的正确性。 而这样得到的集合大小是不超过 $2S$ 的,那么我们得到了一个 $O(qS\log n+\frac{n^2}{S})$ 的做法,令 $S=\frac{n}{\sqrt{q\log n}}$ 就是 $O(n\sqrt{q\log n})$ 了。 实际上这个 $\log$ 可以去掉。我们离线,把询问挂在对应大集合上。 扫两遍,分开维护向左取的 $y$ 和向右取的 $y$,每次用一个栈维护若干个连续的连续段集合,判断新加入的集合能否和栈顶合并显然可以 $O(1)$。最后我们将每个连续段内部的 $y$ 以及其向左(或向右)还能扩展的 $y$ 加入集合即可。 这样的复杂度就是 $O(qS+\frac{n^2}{S})$ 了,令 $S=\frac{n}{\sqrt q}$ 即可得到 $O(n\sqrt q)$ 的复杂度。 ## 代码 ``` #include<stdio.h> #include<string.h> #include<vector> #include<algorithm> #include<map> char buf[1<<23],*p1=buf,*p2=buf,obuf[1<<23],*O=obuf; #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<23,stdin),p1==p2)?EOF:*p1++) using namespace std; const int maxn=300005,maxq=1000005,S=300; int n,m,ps,top,tmps,stp; int a[maxn],b[maxn],stk[maxn][3],pos[maxn],tmp[maxn],tmpp[maxn],c[maxn],xx[maxq],yy[maxq],cnt[maxn],vis[maxn]; long long inf; long long ans[maxq],mn[maxn<<1]; vector<int>v[maxn],q[maxn]; map<pair<int,int>,long long>mp; long long solve(int x,int y){ for(int i=1;i<=ps;i++) c[i]=c[i-1]+(a[pos[i]]==x? 1:-1); long long res=-1e18,s=0; mn[ps]=0; for(int i=1;i<=ps;i++) s+=b[pos[i]],res=max(res,s-mn[c[i]+ps]),mn[c[i]+ps]=min(mn[c[i]+ps],s); for(int i=0;i<=ps;i++) mn[c[i]+ps]=inf; return res; } void read(int &x){ x=0; char c=getchar(); int f=0; for(;c<'0'||c>'9';c=getchar()) f|=(c=='-'); for(;c>='0'&&c<='9';c=getchar()) x=x*10+c-48; if(f==1) x=-x; } void print(long long x){ if(x<0) putchar('-'),x=-x; if(x>9) print(x/10); putchar(x%10+48); } int main(){ // freopen("tmp.in","r",stdin); // freopen("tmp.out","w",stdout); memset(mn,127,sizeof(mn)),inf=mn[0]; read(n),read(m); for(int i=1;i<=n;i++) read(a[i]),v[a[i]].push_back(i); for(int i=1;i<=n;i++) read(b[i]); for(int t=1,x,y;t<=m;t++){ read(x),read(y),xx[t]=x,yy[t]=y; if((v[x].size()>S)+(v[y].size()>S)==1){ if(v[x].size()>S) q[x].push_back(t); else q[y].push_back(t); continue; } if(mp.count(make_pair(min(x,y),max(x,y)))){ ans[t]=mp[make_pair(min(x,y),max(x,y))]; continue; } int i=0,j=0; ps=0; while(i<v[x].size()||j<v[y].size()){ if(i<v[x].size()&&(j==v[y].size()||v[x][i]<v[y][j])) pos[++ps]=v[x][i],i++; else pos[++ps]=v[y][j],j++; } ans[t]=mp[make_pair(min(x,y),max(x,y))]=solve(x,y); } for(int t=1;t<=n;t++) if(v[t].size()>S&&q[t].size()){ for(int i=1;i<=n;i++) cnt[i]=cnt[i-1]+(a[i]==t); for(int p=0;p<q[t].size();p++){ int k=q[t][p],r=xx[k]+yy[k]-t; ps=top=tmps=0,stp++; for(int i=0;i<v[r].size();i++) pos[++ps]=v[r][i]; for(int i=0;i<v[r].size();i++){ int j=i; while(j+1<v[r].size()&&cnt[v[r][j+1]-1]==cnt[v[r][j]]) j++; int p=v[r][i],q=v[r][j],c=j-i+2; while(top){ int w=cnt[p-1]-cnt[stk[top][2]]; if(w<=c) p=stk[top][1],c=c+stk[top][3]-w,top--; else break; } top++,stk[top][0]=c,stk[top][1]=p,stk[top][2]=q,i=j; } int lst=0; for(int i=1;i<=top;i++){ int c=stk[i][0],p=stk[i][1],q=stk[i][2]; lst=max(lst,cnt[p]-c); while(lst<=cnt[q]-1){ if(vis[v[t][lst]]!=stp) tmp[++tmps]=v[t][lst],vis[v[t][lst]]=stp; lst++; } } merge(pos+1,pos+1+ps,tmp+1,tmp+1+tmps,tmpp+1); int tmpps=ps+tmps; tmps=top=0; for(int i=v[r].size()-1;i>=0;i--){ int j=i; while(j>=1&&cnt[v[r][j-1]]==cnt[v[r][j]-1]) j--; int p=v[r][j],q=v[r][i],c=i-j+2; while(top){ int w=cnt[stk[top][1]-1]-cnt[q]; if(w<=c) q=stk[top][2],c=c+stk[top][3]-w,top--; else break; } top++,stk[top][0]=c,stk[top][1]=p,stk[top][2]=q,i=j; } lst=cnt[n]-1; for(int i=1;i<=top;i++){ int c=stk[i][0],p=stk[i][1],q=stk[i][2]; lst=min(lst,cnt[q]+c-1); while(lst>=cnt[p]){ if(vis[v[t][lst]]!=stp) tmp[++tmps]=v[t][lst],vis[v[t][lst]]=stp; lst--; } } reverse(tmp+1,tmp+1+tmps),merge(tmpp+1,tmpp+1+tmpps,tmp+1,tmp+1+tmps,pos+1); ps=tmpps+tmps,ans[k]=solve(t,r); } } for(int i=1;i<=m;i++) print(ans[i]),putchar('\n'); return 0; } ```