[JZOJ4687] C题 奇袭 分治
题目描述
输入格式
输出格式
样例
数据范围与提示
solution:
对于这道题,我承认我看了半天提解,又看了std才过
首先介绍27%算法:O(n5)暴力,这个非常好想,枚举横,纵坐标及区间长度,再judge就好了
#include<iostream> #include<cstdio> #include<cstring> #include<vector> #define re register #define MAXN 50005 using namespace std; int n,ans=0; vector<int>v[MAXN]; inline bool judge(re int x1,re int y1,re int x2,re int y2,re int num){ int tot=0; for(re int i=x1;i<=x2;i++){ re int m=v[i].size(); for(re int j=0;j<m;j++){ if(tot>num) return 0; if(v[i][j]>=y1&&v[i][j]<=y2) tot++; } } return (tot==num); } signed main(){ scanf("%d",&n); for(re int i=1,x,y;i<=n;i++){ scanf("%d%d",&x,&y); v[x].push_back(y); } for(re int i=1;i<=n;i++){ for(re int j=1;j<=n;j++){ for(re int k=1;k<=n;k++){ re int h=i+k-1,l=j+k-1; if(h>n||l>n) break; if(judge(i,j,h,l,k)) ans++; } } } printf("%d\n",ans); return 0; }
64%:我们发现一个美妙性质:在区间[l,r]中,只要满足max(a[i])-min(a[j])=r-l(),ans就可以++
#include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<deque> #define re register #define MAXN 50005 using namespace std; int n,ans=0,a[MAXN]; signed main(){ scanf("%d",&n); for(re int i=1,x,y;i<=n;i++){ scanf("%d%d",&x,&y); a[x]=y;//第x行第y列有军队 } for(int l=1;l<=n;l++){ int maxx=0,minn=0x7fffffff; for(int r=l;r<=n;r++){ maxx=max(a[r],maxx),minn=min(a[r],minn); if(maxx-minn==r-l) ans++; } } printf("%d\n",ans); return 0; }
91%:在64%加一个大减枝,具体的见代码:
#include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<deque> #define re register #define MAXN 50005 using namespace std; int n,ans=0,a[MAXN],b[MAXN]; signed main(){ scanf("%d",&n); for(re int i=1,x,y;i<=n;i++){ scanf("%d%d",&x,&y); a[x]=y;//第x行第y列有军队 b[y]=x; } for(int l=1;l<=n;l++){ int maxx=0,minn=0x7fffffff; for(int r=l;r<=n;r++){ if(b[a[r]-1]<l&&b[a[r]+1]<l){ if(l==r) ans++; break; } maxx=max(a[r],maxx),minn=min(a[r],minn); if(maxx-minn==r-l) ans++; } } printf("%d\n",ans); return 0; }
91分你是无论如何也A不掉
我们需要分治(感谢moyii学姐的思路)
work(l,r)求l到r区间里答案的个数,左区间的答案和右区间的答案是可以再向下分的,那么需要在这个函数里求的就是跨中线的答案了。
定义:max_l[i]、min_l[i]:在[i,mid]区间内的最大、最小a[i];max_r[i]、min_r[i]:在[mid+1,i]区间内的最大、最小a[i]。
按区间最大最小值的位置分四种情况:
max、min都在左边:从mid向l枚举i,如果区间[i,j]是一个合法解则应满足
max_l[i]-min_l[i]=j-i,max_r[j]<max_l[i],mid<j<=r,min_r[j]>min_l[i]。
找到合法解就把ans++。
max、min都在右边同理。
min在左边,max在右边:从mid向l枚举i,如果区间[i,j]是一个合法解则应满足max_r[j]-min_l[i]=j-i,
移项得max_r[j]-j=min_l[i]-i,
这样我们只需要统计从mid+1到r所有的max_r[j]-j对应的可行解个数就可以通过min_l[i]-i来得到i对应的解了,这可以用桶实现。
max_r当然是单调递增的,而min_r是单调递减了,用两个指针f、t从mid+1向r移动,
当max_r[f]<max_l[i]时右移f且tong[max_r[f]-f]--,
当min_r[t]>min_l[i]时右移t且tong[max_r[t]-t]++,
容易看出f经过的都是不可行的点而t经过的都是可行的点,对于每一个i移动两个指针完毕后ans+=tong[min_l[i]-i]。
max在左边,min在右边同理。
减会出现负数,加上n即可
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<vector> 5 #include<deque> 6 #include<algorithm> 7 #define re register 8 #define MAXN 50005 9 using namespace std; 10 int n,ans=0,a[MAXN]; 11 int min_l[MAXN],min_r[MAXN],max_l[MAXN],max_r[MAXN],tong[MAXN<<1]; 12 int get_res(int l,int r,int mid){ 13 int res=0; 14 max_l[mid]=min_l[mid]=a[mid],max_r[mid+1]=min_r[mid+1]=a[mid+1]; 15 for(int i=mid-1;i>=l;i--){ 16 max_l[i]=max(max_l[i+1],a[i]); 17 min_l[i]=min(min_l[i+1],a[i]); 18 } 19 for(int i=mid+2;i<=r;i++){ 20 max_r[i]=max(max_r[i-1],a[i]); 21 min_r[i]=min(min_r[i-1],a[i]); 22 } 23 for(int i=l;i<=mid;i++){ 24 int j=i+max_l[i]-min_l[i]; 25 if(j>mid&&j<=r&&max_l[i]>=max_r[j]&&min_l[i]<=min_r[j]) 26 res++; 27 } 28 int L=mid,R=mid; 29 while(R>=l&&min_l[R]>min_r[r]){ 30 tong[min_l[R]-R+n]--; 31 R--; 32 } 33 while(L>=l&&max_l[L]<max_r[r]){ 34 tong[min_l[L]-L+n]++; 35 L--; 36 } 37 for(int i=r;i>=mid+1;i--){ 38 while(L<mid&&max_l[L+1]>max_r[i]) 39 L++,tong[min_l[L]-L+n]--; 40 while(R<mid&&min_l[R+1]<min_r[i]) 41 R++,tong[min_l[R]-R+n]++; 42 if(tong[max_r[i]-i+n]>0) 43 res+=tong[max_r[i]-i+n]; 44 } 45 for(int i=l;i<=mid;i++) 46 tong[min_l[i]-i+n]=0; 47 return res; 48 } 49 int work(int l,int r){ 50 if(l==r) return 1; 51 int mid=(l+r)>>1; 52 int res=work(l,mid)+work(mid+1,r)+get_res(l,r,mid); 53 if((r-l+1)&1) mid--; 54 reverse(a+l,a+r+1); 55 res+=get_res(l,r,mid); 56 reverse(a+l,a+r+1); 57 return res; 58 } 59 signed main(){ 60 scanf("%d",&n); 61 for(int i=1,x,y;i<=n;i++){ 62 scanf("%d%d",&x,&y); 63 a[x]=y; 64 } 65 ans=work(1,n); 66 printf("%d\n",ans); 67 return 0; 68 }