T1:礼物
水题一道,谁爆零谁傻逼,谁考完后5minA谁智障
(天皇:我15min就A了,期望dp不倒着就是傻;DeepinC:你说啥?)
T2:通讯
tarjan模板,先缩点,贪心稍微需要思考,只要对不是0所在强联通分量的点取一条最小入边就好了
(cbx曰:谁爆零谁cbx)
T3:奇袭 神题标记
考试的时候刚开始没思路,就想着打个n^5暴力,然后打着打着发现了一些性质,删了层循环,然后又删了层循环,又删了层循环
然后开心地得到了n^2的解法,64分拿到
然后就死了,并不知道接下来怎么做,64分钉死
这题用到了几个很久之前学过的东西,一个从来没学过的东西
学过的:分治,单调栈 没学过:桶
首先我们观察n^2的式子,if(max(hi,hi+1......hj)-min(hi,hi+1......hj)+1==j-i+1) ans++,hi表示第i行的军队在第几列
由于ans上界10亿,单纯ans++肯定过不了,考虑分治
solve(l,r)表示l到r的区间有多少贡献显然可以以中点为界递归求解,现在我们只需要求解跨mid的情况
这种情况主要分为两种:1,最小值和最大值都在一边
2,最小值和最大值在两边
对第一种情况,这里以在左边为例,我们利用单调栈(感性理解)的思想,处理出来mid左右每个位置到mid的最大最小值
然后我们扫左边(l,mid)所有点,能得到可能的右端点,然后判断是否成立
即对于左边的一个点i,令j=i+lmax[i]-lmin[i],若rmin[j]>=lmin[i]&&rmax[j]<=lmax[i],还有j>mid&&j<=r
扫到成立的点直接 ans++,单次复杂度O(r-l+1),总复杂度O(nlogn)
然后就是优美(?)的最大最小分边情况了,这里我们只考虑最小值在左,最大值在右的情况
我们发现若一个区间(i,j)满足要求,则有max(hmid+1,hmid+2......hj)-min(hi,hi+1......hmid)==j-i
然后我们把他移项,就得到了max(hmid+1,hmid+2......hj)-j==min(hi,hi+1......hmid)-i
用我们求出来的东西表示即为lmin[i]-i==rmax[j]-j
只要满足最大值在右,最小值在左,那左边和右边无关,这让我们想到了一个东西,桶(虽然我没想到)
我们把右边的东西放进桶里,然后用左边的去查询就好了,现在我们只需要找到每个i对应的合适区间就好了
我们根据max,min的单调性,可以发现每个i对应的区间都是连续的,如果暴力我们可以二分(那就和桶没什么关系了),但是我们发现每个i对应的区间也是单调的
我们可以让两个指针维护当前可行区间,设L为当前左区间,R为当前右区间
先让L=R=mid,然后让L移到对于r最左不可行位置,同时让桶里的值++,然后让R左移到对于r最右可行位置,同时把不符合的情况--
然后从扫(mid+1,mid)每个点,让L,R移到可行位置同时更新桶里的值,如果t[rmax[i]-i]>0,ans+=t[rmax[i]-i]
最后就得到了跨区间的答案,然后我们发现桶太大,如果每次都memset时间太慢
但是我们发现只用(l,mid)的值更新了桶,所以再扫一遍(l,mid),把桶里对应值归零就好了
如果要处理相反情况,可以直接把区间翻转,网上好多都说加reverse会提高复杂度,但是reserve一次复杂度O(len),所以总复杂度没变,记得处理中点
单次复杂度O(r-l+1),总复杂度O(nlogn)
最终复杂度O(nlogn)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int h[50005],ans,lmax[50005],lmin[50005],rmax[50005],rmin[50005],tong[100005],*t=tong+50000; inline int get(int l,int r,int mid){ int ans=0; lmax[mid]=lmin[mid]=h[mid],rmax[mid+1]=rmin[mid+1]=h[mid+1]; for(int i=mid-1;i>=l;i--){ lmax[i]=max(lmax[i+1],h[i]); lmin[i]=min(lmin[i+1],h[i]); } for(int i=mid+2;i<=r;i++){ rmax[i]=max(rmax[i-1],h[i]); rmin[i]=min(rmin[i-1],h[i]); } for(int i=l,j=i+lmax[i]-lmin[i];i<=mid;i++,j=i+lmax[i]-lmin[i]) if(j>mid&&j<=r&&lmax[i]>=rmax[j]&&lmin[i]<=rmin[j]) ans++; int R=mid,L=mid; while(R>=l&&lmin[R]>rmin[r]) t[lmin[R]-R]--,R--; while(L>=l&&lmax[L]<rmax[r]) t[lmin[L]-L]++,L--; for(int i=r;i>=mid+1;i--){ while(L<mid&&lmax[L+1]>rmax[i]) L++,t[lmin[L]-L]--; while(R<mid&&lmin[R+1]<rmin[i]) R++,t[lmin[R]-R]++; if(t[rmax[i]-i]>0) ans+=t[rmax[i]-i]; } for(int i=l;i<=mid;i++) t[lmin[i]-i]=0; return ans; } int solve(int l,int r){ if(l==r) return 1; int mid=l+r>>1; int ans=solve(l,mid)+solve(mid+1,r)+get(l,r,mid); reverse(h+l,h+r+1); ans+=get(l,r,mid-((r-l+1)&1)); reverse(h+l,h+r+1); return ans; } int main(){ int n,x,y; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&x,&y),h[x]=y; printf("%d",solve(1,n)); return 0; }