20190716-T3-奇袭

我要嗝了

我经过一系列努力,寻找了一系列,各种复杂度的方法。

1>纯暴力

复杂度:$\Theta(N^5)$

不多解释,上代码:

空间复杂度无法承受,如果考试偏要写这个不妨动态开数组:

例:

#include<iosteam>
using namespace std;
int n;
int *Array;//开一个指针
int main(){
    cin>>n;
    Array=new int[n];//像这样
}

全码

 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 }
View Code

2>一般人想的到的暴力

复杂度:$\Theta(N^4)$

枚举长度$\Theta(N)$,开头$\Theta(N^2)$,再判断$\Theta(N)$。

 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 }
View Code

3>一棵二维线段树(我疯了)

复杂度:$\Theta(N^3 \log_4 N+N\log_4N)$

常数巨大,T到飞起……9分好成绩(我猜有的点输入时就直接T飞)

二维线段树插入点插入一个点后这个树就变成这样(每一个矩形,包括已经被切开的点都是一个节点)

一个节点有四个儿子(左上,左下,右上,右下),如果不动态开点原地MLE,如果动态开点T

 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 }
A Hyper Long Code

4>二维前缀和

复杂度:$\Theta(N^3)$

空间惊人,开不了那么大,考试想到了就可着部分分去吧

 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 }
View Code

5>线段树优化一拨

我们在这里发现了这个题的真实题面:给定N个数的一个排列,问这个序列中有多少个子区间的数恰好是连续的。

进一步可以化为:有多少种情况使得,相邻的k个数中最大值和最小值的差小于等于k-1。

复杂度:$\Theta(N^2 \log N)$

还不错,没试能得几分

 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 }
View Code

6>(借的)别人的暴力(?)

复杂度:$\Theta(N^2)$

很优秀了,蒟蒻OTZ

 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卡常,应该能看出来
View Code DeepinC

7>终于到了正解%%%

复杂度:使用 reverse(); $\Theta(N\log^{2}N)$

不用:$\Theta(N\log N)$

这个正解我好像解释不清楚~~

我尽量

首先我们可以想到分治,

把区间分成两部分,当前的区间答案就可以表示为$ans_{[l,mid]}+ans_{[mid+1,r]}+$跨区间方案

你很好想前两部分如何写(递归)

问题就出在跨区间这里

有两种,

最值在一边:

  我纳过闷,最值在一边,怎么跑另一边????

但是:可以这样:

OXXX
XXOX
XXXO
XOXX

表示成一维:$\{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;

终于让我水了它……

 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 }
AC---View Code

 

转载于:https://www.cnblogs.com/kalginamiemeng/p/Exam20190716T3.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值