题解:P6579 [Ynoi2019] Happy Sugar Life

· · 题解

Solution

唉分享一下我的做法,可能比较麻烦。不过确实好想。

这种题只需要想到分块可能就做完了。 /kel

(i,p_i) 描到二维平面上,问的就是一个矩形里包含了多少个上升二元对。

4-side 矩形不好处理,考虑拆成 2-side 矩形,如图所示:

一个 2-side 矩形里面包含多少个上升对是容易计算的,跑两边二维数点就行。

容斥一遍之后,只有形如 CCCACDCB 的二元组还存在。

接着考虑 3-side 矩形能给我们带来什么。比如用 $CA$ 构成的 3-side 矩形的答案,减去 A 构成的 3-side 矩形的答案,得到的是 $CA+CC$。同理我们可以得到 $CD+CC$。而 $(CA+CC)+(CD+CC)-(CA+CD+CC) = CC$,也就是我们会做 3-side 矩形这道题就结束了。不妨只考虑向纵轴正方向开口的矩形。 套路的进行分块。考虑进行**扫描线**,按照纵坐标($y$)从大王小加入点,然后询问一个区间内的上升对个数。 不就这几种贡献吗: 1. 整块内。加入一个点之后,暴力在块内将和它有关的上升二元组加入即可。维护当前前 $i$ 个块里有多少个数 $cnt$,后面要用到。 2. 整块间。记二元组 $(i,j)$ 表示第 $i$ 个块到第 $j$ 个块的答案。发现新增一个数,会进行 $O(\sqrt n)$ 次对 $i \le L$,$j = R$ 的二元组 $(i,j)$ 整体加上一个相同的值。(有一个非常重要的事实:我们每次加入的都是**当前区间的最小数**,所以只需要考虑右边比他大的)新增的这个量可以用 $cnt$ 算出。 3. 散块内部。需要知道当前一个块内前缀或后缀的上升二元组个数。这个每次修改也只会影响 $O(\sqrt n)$ 个位置的值,暴力算一遍就行。 4. 散块之间。由于块内相对顺序不会改变,所以排好序之后归并一下就行。特别地,对于询问在同一个块内的情况,用两个前缀之差减去归并的结果。 5. 散块和整块。对于整块左边的位置而言,在它加入的时候算一下整块有多少个数;对于整块右边的位置而言,它加入的时候算一下整块有多少个数,然后在询问的时候再算一下整块有多少个数,作差即得贡献。 这样可以做到线性空间,$O(m \sqrt n)$ 的复杂度。稍微调一下块长就可以过这个题,当然无压力通过时代的眼泪。 不用太多 vector 就能过。 代码(有点丑。。) ```cpp #include<bits/stdc++.h> #define ll long long #define ffor(i,a,b) for(int i=(a);i<=(b);i++) #define roff(i,a,b) for(int i=(a);i>=(b);i--) using namespace std; const int MAXN=2e5+10; int n,m,p[MAXN],X1[MAXN],X2[MAXN],Y1[MAXN],Y2[MAXN],rev[MAXN]; ll ans[MAXN],tcnt[MAXN],tcnt1[MAXN],tcnt2[MAXN]; struct QR1 {int id,y,op;}; vector<QR1> qr1[MAXN]; ll tr[MAXN]; inline void update(int pos,const int v) {pos=n-pos+1;while(pos<=n) tr[pos]+=v,pos+=pos&-pos;return ;} inline ll query(int pos) {ll ans=0;pos=n-pos+1;while(pos) ans+=tr[pos],pos-=pos&-pos;return ans;} struct QR2 {int l,r,id;}; const int B=300,BB=B+10; int L[MAXN],R[MAXN],bel[MAXN],k,pre[MAXN],suf[MAXN],cnt[MAXN],ins[MAXN]; //pre 表示前缀,suf 表示后缀, ll add[MAXN/B][MAXN/B]; int tmtp[MAXN],st[MAXN/B][BB],len[MAXN/B]; pair<int,QR2> qr2[MAXN*2]; struct QR3 {int l,r,y,yy,id;}; int etot,mtot; pair<int,QR3> ex[MAXN*2]; inline void add_pos(const int pos,const int val) { int bl=bel[pos],tmp=0; ffor(j,bl,k) cnt[j]++,add[bl][j]+=cnt[j]-cnt[bl]; ffor(j,pos+1,R[bl]) {if(p[j]>val) ins[bl]++,tmp++;pre[j]+=tmp;} roff(j,pos,L[bl]) suf[j]+=tmp; return ; } inline int calc(const int l1,const int r1,const int l2,const int r2,const int val) { int pos=0,ans=0,cnt=0; ffor(i,1,len[bel[l2]]) { int id=st[bel[l2]][i]; if(l2<=id&&id<=r2&&p[id]>=val) { while(pos<len[bel[l1]]) { int idx=st[bel[l1]][pos+1]; if(p[idx]>p[id]) break ; if(l1<=idx&&idx<=r1&&p[idx]>=val) cnt++; pos++; } ans+=cnt; } } return ans; } int ss[MAXN],ee[MAXN]; void I_AK_NOI2020_Day1(void) { ffor(i,1,n) rev[p[i]]=i; k=(n+B-1)/B; ffor(i,1,k) L[i]=R[i-1]+1,R[i]=min(n,i*B); ffor(i,1,k) { len[i]=0; ffor(j,L[i],R[i]) bel[j]=i,tmtp[++len[i]]=j; sort(tmtp+1,tmtp+len[i]+1,[&](int A,int B) {return p[A]<p[B];}); ffor(j,1,len[i]) st[i][j]=tmtp[j]; } etot=mtot=0; ffor(i,1,m) { qr2[++mtot]={Y1[i],{X1[i],X2[i],i}},qr2[++mtot]={Y2[i]+1,{X1[i],X2[i],-i}}; if(bel[X1[i]]!=bel[X2[i]]) ex[++etot]={X1[i],{X1[i],X2[i],Y1[i],Y2[i]+1,i}},ex[++etot]={X2[i]+n,{X1[i],X2[i],Y1[i],Y2[i]+1,i}}; } sort(qr2+1,qr2+mtot+1,[](pair<int,QR2> A,pair<int,QR2> B) {return A.first<B.first;}); sort(ex+1,ex+etot+1,[](pair<int,QR3> A,pair<int,QR3> B) {return A.first<B.first;}); memset(ss,0x3f,sizeof(ss)),memset(ee,0,sizeof(ee)); ffor(i,1,etot) { ss[ex[i].first]=min(ss[ex[i].first],i); ee[ex[i].first]=i; } ffor(i,1,n+n) ee[i]=max(ee[i],ee[i-1]); roff(i,n+n,1) ss[i]=min(ss[i],ss[i+1]); while(mtot&&qr2[mtot].first>n) mtot--; roff(i,n,1) { add_pos(rev[i],i); while(mtot&&qr2[mtot].first==i) { auto pr=qr2[mtot].second; mtot--; int l=pr.l,r=pr.r,id=pr.id; if(bel[l]==bel[r]) { int ad=pre[r]; if(l!=L[bel[l]]) ad-=pre[l-1]; ad-=calc(L[bel[l]],l-1,l,r,i); if(id>0) ans[id]+=ad; else ans[-id]-=ad; } else { int lid=bel[l]+1,rid=bel[r]-1; ll ad=0; if(lid<=rid) ffor(j,lid,rid) ad+=add[j][rid]+ins[j]; ad+=calc(l,R[bel[l]],L[bel[r]],r,i)+suf[l]+pre[r]; int mzx=cnt[rid]-cnt[lid-1]; ffor(j,L[bel[r]],r) if(p[j]>=i) ad+=mzx; if(id>0) ans[id]+=ad; else ans[-id]-=ad; } } int pos=rev[i]; ffor(j,ss[L[bel[pos]]],ee[pos]) { auto pr=ex[j].second; if(pr.y<=i&&pr.yy>i) { int id=pr.id,lid=bel[pos]+1,rid=bel[pr.r]-1; ans[id]+=cnt[rid]-cnt[lid-1]; } } ffor(j,ss[pos+n],ee[R[bel[pos]]+n]) { auto pr=ex[j].second; if(pr.y<=i&&pr.yy>i) { int id=pr.id,lid=bel[pr.l]+1,rid=bel[pos]-1; ans[id]-=cnt[rid]-cnt[lid-1]; } } } return ; } int main() { ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); cin>>n>>m; ffor(i,1,n) cin>>p[i]; ffor(i,1,m) cin>>X1[i]>>X2[i]>>Y1[i]>>Y2[i],qr1[X1[i]].push_back({i,Y1[i],1}),qr1[X1[i]].push_back({i,Y2[i]+1,2}),qr1[X2[i]+1].push_back({i,Y1[i],2}),qr1[X2[i]+1].push_back({i,Y2[i]+1,3}); roff(i,n,1) { tcnt[i]=query(p[i]),update(p[i],1); for(auto qr:qr1[i]) if(qr.op==1) tcnt1[qr.id]+=query(qr.y); else if(qr.op==2) tcnt1[qr.id]-=query(qr.y); else tcnt1[qr.id]+=query(qr.y),tcnt2[qr.id]=query(qr.y); } ffor(i,1,m) ans[i]=-tcnt1[i]*tcnt2[i]; memset(tr,0,sizeof(tr)); roff(i,n,1) { update(p[i],tcnt[i]); for(auto qr:qr1[i]) if(qr.op==1||qr.op==3) ans[qr.id]+=query(qr.y); else ans[qr.id]-=query(qr.y); } ffor(i,1,m) ans[i]=-ans[i]; I_AK_NOI2020_Day1(); memset(pre,0,sizeof(pre)),memset(suf,0,sizeof(suf)),memset(add,0,sizeof(add)),memset(cnt,0,sizeof(cnt)),memset(ins,0,sizeof(ins)); ffor(i,1,n) rev[p[i]]=i; ffor(i,1,n) p[i]=rev[i]; ffor(i,1,m) swap(X1[i],Y1[i]),swap(X2[i],Y2[i]); I_AK_NOI2020_Day1(); ffor(i,1,m) cout<<ans[i]<<'\n'; return 0; } ```