我要嗝了
我经过一系列努力,寻找了一系列,各种复杂度的方法。
1>纯暴力
复杂度:$\Theta(N^5)$
不多解释,上代码:
空间复杂度无法承受,如果考试偏要写这个不妨动态开数组:
例:
#include<iosteam> using namespace std; int n; int *Array;//开一个指针 int main(){ cin>>n; Array=new int[n];//像这样 }
全码
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #define N 5000 6 using namespace std; 7 int le,ans; 8 bool ma[N][N]; 9 int main(){ 10 //freopen("raid.in","r",stdin); 11 //freopen("raid.out","w",stdout); 12 int a,b; 13 scanf("%d",&le); 14 for (int i=1;i<=le;i++) 15 scanf("%d%d",&a,&b),ma[a][b]=1; 16 for (int k=0;k<le;k++){ 17 for (int i=1;i<=le;i++){ 18 for (int j=1;j<=le;j++){ 19 int cnt=0; 20 for (int x=1;x<=le;x++){ 21 for (int y=1;y<=le;y++){ 22 if(ma[x][y])cnt++; 23 } 24 } 25 if(cnt==k+1)ans++; 26 } 27 } 28 } 29 cout<<ans<<endl; 30 return 0; 31 }
2>一般人想的到的暴力
复杂度:$\Theta(N^4)$
枚举长度$\Theta(N)$,开头$\Theta(N^2)$,再判断$\Theta(N)$。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #define N 50001 6 using namespace std; 7 int le,x[N],y[N],ans; 8 int main(){ 9 //freopen("raid.in","r",stdin); 10 //freopen("raid.out","w",stdout); 11 int a,b; 12 scanf("%d",&le); 13 for (int i=1;i<=le;i++) 14 scanf("%d%d",&x[i],&y[i]); 15 for (int k=0;k<le;k++){ 16 for (int i=1;i<=le;i++){ 17 for (int j=1;j<=le;j++){ 18 int num=0; 19 for (int t=1;t<=le;t++){ 20 if(i<=x[t]&&j<=y[t]&&x[t]<=i+k&&y[t]<=j+k){ 21 num++; 22 } 23 } 24 if(num==k+1)ans++; 25 } 26 } 27 } 28 cout<<ans<<endl; 29 return 0; 30 }
3>一棵二维线段树(我疯了)
复杂度:$\Theta(N^3 \log_4 N+N\log_4N)$
常数巨大,T到飞起……9分好成绩(我猜有的点输入时就直接T飞)
插入一个点后这个树就变成这样(每一个矩形,包括已经被切开的点都是一个节点)
一个节点有四个儿子(左上,左下,右上,右下),如果不动态开点原地MLE,如果动态开点T飞
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 //#include "debug.h" 6 #define N 50001 7 using namespace std; 8 int le; 9 struct XDS{ 10 int dat; 11 XDS* rd,*ru,*ld,*lu; 12 XDS(){ 13 dat=0; 14 rd=ru=lu=ld=NULL; 15 } 16 }*root; 17 void add(XDS *&rt,int x,int y,int xl,int xr,int yl,int yr){ 18 if(rt==NULL)rt=new XDS(); 19 if(xl==xr&&yl==yr){ 20 rt->dat++; 21 return ; 22 } 23 int xmid=(xl+xr)>>1,ymid=(yl+yr)>>1; 24 if (x>xmid &&y>ymid ) add(rt->rd,x,y,xmid+1,xr ,ymid+1,yr); 25 else if(x>xmid &&y<=ymid) add(rt->ru,x,y,xmid+1,xr ,yl ,ymid); 26 else if(x<=xmid&&y>ymid ) add(rt->ld,x,y,xl ,xmid,ymid+1,yr); 27 else add(rt->lu,x,y,xl ,xmid,yl ,ymid); 28 int sum=0; 29 if(rt->rd!=NULL) sum+=rt->rd->dat; 30 if(rt->ru!=NULL) sum+=rt->ru->dat; 31 if(rt->ld!=NULL) sum+=rt->ld->dat; 32 if(rt->lu!=NULL) sum+=rt->lu->dat; 33 rt->dat=sum; 34 } 35 int ask(XDS *rt,int axl,int axr,int ayl,int ayr,int xl,int xr,int yl,int yr){ 36 if(rt==NULL)return 0; 37 if(axl<=xl&&axr>=xr&&ayl<=yl&&ayr>=yr) return rt->dat; 38 int none=0; 39 int xmid=(xl+xr)>>1,ymid=(yl+yr)>>1; 40 none+=ask(rt->rd,axl,axr,ayl,ayr,xmid+1,xr ,ymid+1,yr); 41 none+=ask(rt->ru,axl,axr,ayl,ayr,xmid+1,xr ,yl ,ymid); 42 none+=ask(rt->ld,axl,axr,ayl,ayr,xl ,xmid,ymid+1,yr); 43 none+=ask(rt->lu,axl,axr,ayl,ayr,xl ,xmid,yl ,ymid); 44 return none; 45 } 46 int ans=0; 47 int main(){ 48 int a,b; 49 scanf("%d",&le); 50 for (int i=1;i<=le;i++){ 51 scanf("%d%d",&a,&b); 52 add(root,a,b,1,le,1,le); 53 } 54 for (int k=1;k<=le;k++){ 55 for (int i=1;i<=le;i++){ 56 if(i+k-1>le)continue; 57 for (int j=1;j<=le;j++){ 58 if(j+k-1>le)continue; 59 if(ask(root,i,i+k-1,j,j+k-1,1,le,1,le)==k)ans++; 60 } 61 } 62 } 63 cout<<ans<<endl; 64 return 0; 65 }
4>二维前缀和
复杂度:$\Theta(N^3)$
空间惊人,开不了那么大,考试想到了就可着部分分去吧
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 5 //#include "debug.h" 6 7 #define N 1000 8 9 using namespace std; 10 11 int n; 12 bool x[N][N]; 13 int pre[N][N]; 14 int ans=0; 15 int main(){ 16 int a,b; 17 cin>>n; 18 for (int i=1;i<=n;i++){ 19 scanf("%d%d",&a,&b); 20 x[a][b]=1; 21 } 22 for (int i=1;i<=n;i++){ 23 for (int j=1;j<=n;j++){ 24 pre[i][j]=x[i][j]+pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]; 25 } 26 } 27 for (int k=1;k<=n;k++){ 28 for (int i=1;i<=n;i++){ 29 if(i+k-1>n)continue; 30 for (int j=1;j<=n;j++){ 31 if(j+k-1>n)continue; 32 cout<<i<<" "<<j<<endl; 33 if(pre[i+k-1][j+k-1]-pre[i-1][j+k-1]-pre[i+k-1][j-1]+pre[i-1][j-1]==k)ans++; 34 } 35 } 36 } 37 //pour(pre,1,n,1,n,3,"pre"); 38 cout<<ans<<endl; 39 return 0; 40 }
5>线段树优化一拨
我们在这里发现了这个题的真实题面:给定N个数的一个排列,问这个序列中有多少个子区间的数恰好是连续的。
进一步可以化为:有多少种情况使得,相邻的k个数中最大值和最小值的差小于等于k-1。
复杂度:$\Theta(N^2 \log N)$
还不错,没试能得几分
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 //#include "debug.h" 5 #define N 50500 6 using namespace std; 7 int x[N],n; 8 struct XDS{ 9 int maxn,minn; 10 }tr[4*N]; 11 void build(int id,int l,int r){ 12 if(l==r){ 13 tr[id].maxn=tr[id].minn=x[l]; 14 return ; 15 } 16 int mid=(l+r)>>1; 17 build(id*2,l,mid); 18 build(id*2+1,mid+1,r); 19 tr[id].maxn=max(tr[id*2].maxn,tr[id*2+1].maxn); 20 tr[id].minn=min(tr[id*2].minn,tr[id*2+1].minn); 21 } 22 int askmax(int id,int l,int r,int al,int ar){ 23 if(l>=al&&r<=ar)return tr[id].maxn; 24 int val,mid=(l+r)>>1; 25 val=0; 26 if(mid>=al) 27 val=max(val,askmax(id*2 ,l ,mid,al,ar)); 28 if(mid<ar) 29 val=max(val,askmax(id*2+1,mid+1,r ,al,ar)); 30 return val; 31 } 32 int askmin(int id,int l,int r,int al,int ar){ 33 if(l>=al&&r<=ar)return tr[id].minn; 34 int val,mid=(l+r)>>1; 35 val=0x7fffffff; 36 if(mid>=al) 37 val=min(val,askmin(id*2 ,l ,mid,al,ar)); 38 if(mid<ar) 39 val=min(val,askmin(id*2+1,mid+1,r ,al,ar)); 40 return val; 41 } 42 int ans=0; 43 int main(){ 44 int a,b; 45 cin>>n; 46 for (int i=1;i<=n;i++){ 47 scanf("%d%d",&a,&b); 48 x[a]=b; 49 } 50 build(1,1,n); 51 for (int i=1;i<=n;i++){ 52 for (int j=1;j<=n;j++){ 53 if(i<=j){ 54 if(askmax(1,1,n,i,j)-askmin(1,1,n,i,j)==j-i)ans++; 55 } 56 else{ 57 if(askmax(1,1,n,j,i)-askmin(1,1,n,j,i)==j-i)ans++; 58 } 59 //puts("DDD"); 60 } 61 } 62 cout<<ans<<endl; 63 return 0; 64 }
6>(借的)别人的暴力(?)
复杂度:$\Theta(N^2)$
很优秀了,蒟蒻OTZ
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<cstdio> 2 #include<ctime> 3 using namespace std; 4 #define rus register unsigned short 5 inline unsigned short max(const rus a,const rus b){return a>b?a:b;} 6 inline unsigned short min(const rus a,const rus b){return a<b?a:b;} 7 inline unsigned short read(){ 8 rus a=0;register char ch=getchar(); 9 while(ch<48||ch>57)ch=getchar(); 10 while(ch>=48&&ch<=57)a=(a<<3)+(a<<1)+ch-48,ch=getchar(); 11 return a; 12 } 13 int ans; 14 unsigned short pos[50005],n; 15 int main(){ 16 n=read(); 17 for(rus i=1,x,y;i<=n;++i) x=read(),y=read(), pos[x]=y; 18 for(rus i=1,maxx=0,minn=50006;i<=n;++i,maxx=0,minn=50006){ 19 if(clock()>990000){printf("%d\n",ans+n-i+1);return 0;} 20 for(rus j=i;j<=n;++j){ 21 maxx=max(maxx,pos[j]); minn=min(minn,pos[j]); 22 if(maxx-minn==j-i)ans++; 23 } 24 } 25 26 printf("%d\n",ans);//printf("%ld\n",clock()); 27 } 28 29 //考场上DeepinC的n2卡常,应该能看出来
7>终于到了正解%%%
复杂度:使用 reverse(); $\Theta(N\log^{2}N)$
不用:$\Theta(N\log N)$
这个正解我好像解释不清楚~~
我尽量
首先我们可以想到分治,
把区间分成两部分,当前的区间答案就可以表示为$ans_{[l,mid]}+ans_{[mid+1,r]}+$跨区间方案
你很好想前两部分如何写(递归)
问题就出在跨区间这里
有两种,
最值在一边:
我纳过闷,最值在一边,怎么跑另一边????
但是:可以这样:
O | X | X | X |
X | X | O | X |
X | X | X | O |
X | O | X | X |
表示成一维:$\{1,4,2,3\}$
发现可以延伸到那边
我怎么算?
要用这个条件:$\begin{array}{cc}\max\{A_i \cdots A_{mid}\} & > & \max\{A_{mid+1} \cdots A_j \} \\ \min\{A_i \cdots A_{mid}\} & < & \min\{A_{mid+1} \cdots A_j\}\end{array}$其中$i-j=\max-\min$
啊!
最值在两边:单调栈思想?桶
解释不清楚~
上关键代码:
1 /*下面是判最大最小在左右的*/ 2 int ll=mid+1,rr=mid+1;//两个指针记录可行区间,每次我们去搜小的 3 for(int k=mid;k>=l;k--)//左小右大 4 { 5 while(minn[rr]>minn[k]&&rr<=r) check(maxn[rr]-rr)++,rr++;//把一段合法区间都标记一下 6 while(maxn[ll]<maxn[k]&&ll<rr) check(maxn[ll]-ll)--,ll++;//把非法的情况剪掉 7 ans+=check(minn[k]-k); 8 } 9 while(ll<rr) check(maxn[ll]-ll)--,ll++;//好像????是恢复初始状态 10 ll=mid,rr=mid; 11 for(int k=mid+1;k<=r;k++)//左大右小 12 { 13 while(minn[rr]>minn[k]&&rr>=l) check(maxn[rr]+rr)++,rr--;//同上 14 while(maxn[ll]<maxn[k]&&ll>rr ) check(maxn[ll]+ll)--,ll--; 15 ans+=check(minn[k]+k); 16 } 17 while(ll>rr) check(maxn[ll]+ll)--,ll--; 18 return ans;
终于让我水了它……
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <iostream> 2 #include <cstdio> 3 #define N 50505 4 #define check(i) tong[(i)+50000] 5 #define Inf 0x7fffffff 6 using namespace std; 7 int n,arr[N],Ans=0; 8 int maxn[N],minn[N]; 9 int tong[3*N]; 10 int divide(int l,int r){//分治 11 if(l==r)return 1;//递归边界 12 int ans=0,mid=(l+r)>>1;//二分 13 ans+=divide(l,mid);//左右的分区间 14 ans+=divide(mid+1,r); 15 maxn[mid]=minn[mid]=arr[mid];//处理i到mid之间的最大最小值 16 maxn[mid+1]=minn[mid+1]=arr[mid+1]; 17 for (int i=mid+2;i<=r;i++){ //处理 18 maxn[i]=max(arr[i],maxn[i-1]); 19 minn[i]=min(arr[i],minn[i-1]); 20 } 21 for (int i=mid-1;i>=l;i--){ 22 maxn[i]=max(arr[i],maxn[i+1]); 23 minn[i]=min(arr[i],minn[i+1]); 24 } 25 for (int i=mid;i>=l;i--){ //处理中间到左边的最值 26 int j=maxn[i]-minn[i]+i; //利用maxn-minn=j-i推的j 27 if(/*1*/j>mid&& //合法区间 28 /*2*/j<=r&& 29 /*3*/maxn[j]<maxn[i]&& //满足maxn-minn 30 /*4*/minn[j]>minn[i]){ 31 ans++; 32 } 33 } 34 for (int i=mid+1;i<=r;i++){ 35 int j=i-maxn[i]+minn[i]; //推的第二个 maxn-minn=i-j 36 if(/*1*/j>=l&& 37 /*2*/j<=mid&& 38 /*3*/maxn[j]<maxn[i]&& //要满足maxn和minn 39 /*4*/minn[j]>minn[i]){ 40 ans++; 41 } 42 } 43 /*下面是判最大最小在左右的*/ 44 int ll=mid+1,rr=mid+1;//两个指针记录可行区间,每次我们去搜小的 45 for(int k=mid;k>=l;k--)//左小右大 46 { 47 while(minn[rr]>minn[k]&&rr<=r) check(maxn[rr]-rr)++,rr++;//把一段合法区间都标记一下 48 while(maxn[ll]<maxn[k]&&ll<rr) check(maxn[ll]-ll)--,ll++;//把非法的情况剪掉 49 ans+=check(minn[k]-k); 50 } 51 while(ll<rr) check(maxn[ll]-ll)--,ll++;//好像????是恢复初始状态 52 ll=mid,rr=mid; 53 for(int k=mid+1;k<=r;k++)//左大右小 54 { 55 while(minn[rr]>minn[k]&&rr>=l) check(maxn[rr]+rr)++,rr--;//同上 56 while(maxn[ll]<maxn[k]&&ll>rr ) check(maxn[ll]+ll)--,ll--; 57 ans+=check(minn[k]+k); 58 } 59 while(ll>rr) check(maxn[ll]+ll)--,ll--; 60 return ans; 61 } 62 int main(){ 63 int a,b; 64 scanf("%d",&n); 65 for (int i=0;i<n;i++){ 66 scanf("%d%d",&a,&b); 67 arr[a]=b; 68 } 69 printf("%d\n",divide(1,n)); 70 return 0; 71 }