[JZOJ4687] C题 奇袭 分治

题目描述

由于各种原因,桐人现在被困在Under World(以下简称UW)中,而UW马上 要迎来最终的压力测试——魔界入侵。

唯一一个神一般存在的Administrator被消灭了,靠原本的整合骑士的力量 是远远不够的。所以爱丽丝动员了UW全体人民,与整合骑士一起抗击魔族。

在UW的驻地可以隐约看见魔族军队的大本营。整合骑士们打算在魔族入侵前 发动一次奇袭,袭击魔族大本营!

为了降低风险,爱丽丝找到了你,一名优秀斥候,希望你能在奇袭前对魔族 大本营进行侦查,并计算出袭击的难度。

经过侦查,你绘制出了魔族大本营的地图,然后发现,魔族大本营是一个N ×N的网格图,一共有N支军队驻扎在一些网格中(不会有两只军队驻扎在一起)。

在大本营中,每有一个k×k(1≤k≤N)的子网格图包含恰好k支军队,我们袭 击的难度就会增加1点。

现在请你根据绘制出的地图,告诉爱丽丝这次的袭击行动难度有多大。

输入格式

第一行,一个正整数N,表示网格图的大小以及军队数量。

接下来N行,每行两个整数,Xi,Yi,表示第i支军队的坐标。

保证每一行和每一列都恰有一只军队,即每一个Xi和每一个Yi都是不一样 的。

输出格式

一行,一个整数表示袭击的难度。

样例

样例输入

5
1 1
3 2
2 4
5 5
4 3

样例输出

10

数据范围与提示

样例解释

显然,分别以(2,2)和(4,4)为左上,右下顶点的一个子网格图中有3支军队,这为我们的难度贡献了1点。类似的子网格图在原图中能找出10个。

数据范围

对于30%的数据,N ≤ 100
对于60%的数据,N ≤ 5000
对于100%的数据,N ≤ 50000

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 }

 

posted @ 2019-07-17 09:47  xukl21  阅读(270)  评论(0编辑  收藏  举报