【HDU5745 2016 Multi-University Training Contest 2M】【二维空间维护 讨论题?排序简化!】Memento Mori 空间范围满足一定排列关系点阵数

Memento Mori

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 132    Accepted Submission(s): 46


Problem Description
Professor Zhang has an   n×m  zero matrix(i.e. a matrix consisting of all 0s). Professor Zhang changes   k  elements in the matrix into 1.

Given a permutation   p  of   {1,2,3,4} , Professor Zhang wants to find the number of such submatrices that:

1. the number of 1s in the submatrix is exactly 4.
2. let the positions of the 1s in the submatrix be   (r1,c1),(r2,c2),(r3,c3),(r4,c4) , then   r1<r2<r3<r4  and   (pipj)(cicj)>0  for all   1i<j4 .
3. no other submatrices inside the submatrix meet the above two conditions.
 

Input
There are multiple test cases. The first line of input contains an integer   T , indicating the number of test cases. For each test case:

The first line contains three integers   n ,   m  and   k   (1n,m,k2000) -- the size of the matrix and the number of 1s. The second line contains four integers   p1,p2,p3,p4  denoting the permutation.

Each of the next   k  lines contains two integers   ri  and   ci   (1rin,1cim)  -- the position of the   i -th 1. No two 1s will be in the same position.
 

Output
For each test case, output an integer denoting answer.
 

Sample Input
  
  
1 5 5 4 1 2 3 4 1 1 2 2 3 3 4 4
 

Sample Output
  
  
1
 

Author
zimpha
 

Source


#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 2020, M = 0, Z = 1e9 + 7, ms63 = 0x3f3f3f3f;
int casenum, casei;
int n, m, K;
int p[6];
struct P
{
	int y, x;
	bool operator < (const P& b)
	{
		if (y != b.y)return y < b.y;
		return x < b.x;
	}
}a[N];
int rk[N];
bool cmp(int x, int y)
{
	return a[x].x < a[y].x;
}
int pre[N];
int nxt[N];
int inlist[N];	//标记一个点是否在有序表中
int line[6];	//line[x]表示第x列的点排在第几行
int LFT, RGT, MID;
bool tryit(int st, int ed)
{
	line[1] = st;
	line[4] = ed;
	line[2] = line[3] = 0;
	if (p[1] > p[4])swap(st, ed);	//现在st的列要严格小于ed的列
	int o = 1;
	int x;
	x = st; for (int i = LFT; i; --i)
	{
		x = pre[x];
		line[++o] = x;
	}if (a[pre[x]].x == a[x].x)return 0;
	x = st; for (int i = MID; i; --i)
	{
		x = nxt[x];
		line[++o] = x;
	}if (nxt[x] != ed)return 0;
	x = ed; for (int i = RGT; i; --i)
	{
		x = nxt[x];
		line[++o] = x;
	}if (a[nxt[x]].x == a[x].x)return 0;
	for (int i = 2; i <= 3; ++i)if (line[i] == 0)return 0;
	if (a[line[2]].y > a[line[3]].y)swap(line[2], line[3]);
	for (int i = 1; i <= 3; ++i)if (a[line[i]].y == a[line[i + 1]].y)return 0;
	for (int i = 1; i < 4; ++i)
	{
		for (int j = i + 1; j <= 4; ++j)
		{
			if ((p[i] - p[j])*(a[line[i]].x - a[line[j]].x) <= 0)return 0;
		}
	}
	return 1;
}
int main()
{
	a[0].x = a[0].y = -1e9;
	scanf("%d", &casenum);
	for (casei = 1; casei <= casenum; ++casei)
	{
		scanf("%d%d%d", &n, &m, &K);
		for (int i = 1; i <= 4; ++i)scanf("%d", &p[i]);
		LFT = min(p[1], p[4]) - 1;
		RGT = 4 - max(p[1], p[4]);
		MID = 2 - LFT - RGT;
		for (int i = 1; i <= K; ++i)scanf("%d%d", &a[i].y, &a[i].x);
		sort(a + 1, a + K + 1);
		for (int i = 1; i <= K; ++i)rk[i] = i;
		sort(rk + 1, rk + K + 1, cmp);
		MS(inlist, 0);
		int ans = 0;
		nxt[0] = pre[0] = 0;
		for (int i = 1; i <= K; ++i)		//按照行从上向下的顺序,做逐点枚举(枚举第一个点)
		{
			int now = 0;
			for (int j = 1; j <= K; ++j)	//按照列从左向右做枚举
			{
				int o = rk[j];
				if (a[o].y >= a[i].y)
				{
					nxt[now] = o;
					pre[o] = now;
					now = o;
					inlist[o] = i;
				}
			}
			nxt[now] = 0;
			int lst = K;
			//按照行行从下到上的顺序,做逐点枚举(枚举第二个点)
			for (int j = K; j > i && a[j].y > a[i].y; --j)
			{
				while (a[lst].y > a[j].y)
				{
					if (inlist[lst] == i)
					{
						int pre_ = pre[lst];
						int nxt_ = nxt[lst];
						nxt[pre_] = nxt_;
						pre[nxt_] = pre_;
					}
					--lst;
				}
				//现在,有序表中的点,纵坐标肯定是在我们所枚举的[ a[i].y, a[j].y ]之间,并且保持着横坐标的不下降性质。
				if (a[j].x == a[i].x)continue;
				ans += tryit(i, j);
			}
		}
		printf("%d\n", ans);
	}
	return 0;
}
/*
【trick&&吐槽】
有的题目,写成讨论真的会很容易出错,不如先取出来然后排序,使得情况简化。

【题意】
给你一个矩阵,矩阵size为n*m(2000*2000)
其中有k个元素为1,其他元素都为0。
现在给你一个大小为4的全排列p{}
我们想要找到一些矩阵,使得——
1,矩阵中1的个数恰好为4。
2,我们记,该矩阵中'1'的位置分别为——
(r1,c1)
(r2,c2)
(r3,c3)
(r4,c4)
并满足 r1<r2<r3<r4 &&
(pi-pj)*(ci-cj)>0对于任意一对(i,j)(i<j)都满足。
3,在这个矩阵内部,没有任何满足1、2的子矩阵。

【类型】
看似复杂讨论 实际暴力维护 + 有序表

【分析】
我们先去研究条件1,2,其意思是——
1,同行同列最多只有一个1
2,我们按照行号从小到大排序 ,
	那么对于列而言
	第1行的列前后位置排p1
	第2行的列前后位置牌p2
	第3行的列前后位置牌p3
	第4行的列前后位置牌p4

再去加上条件3,
意思是,该矩阵刚好被其第一行,第四行,第一列和第四列卡住。

======================================================

这道题的数据规模中,k<=2000,意味着O(k^2)级别的算法是可以AC的。
我们是如何实现这道题的呢?

1,把所有点按照行做排序
2,枚举第一行(外层循环,复杂度O(K))
	3,把所有比第一行靠后的点,都以横坐标从小到大的顺序,用一个链表串联起来。(内层循环,复杂度O(K))
	4,枚举最后一行(内层循环,复杂度O(K))
	5,在枚举最后一行的同时,利用双指针(该双指针初始在最后一行),把所有行比最后一行大的都删除。
	6,这样,目前的所有行,都在第一行和最后一行的范围内。而且按照列坐标从小到大的顺序排序。
	7,我们现在已经确定了第一行和最后一行。并且可以通过p[],知道这两行是对应着4列中的第几列。
		也就知道了,这两列中间还有几列,前后还有几列。
		然后我们可以把这中间的列和前后的列都找出来。(如果发现数量不对就GG了)
		然后对于这些点,我们要满足——
		r1<r2<r3<r4 && (pi-pj)*(ci-cj)>0对于任意一对(i,j)(i<j)都满足。
		这个呀,我们加一些特判条件就好啦。

【时间复杂度&&优化】
O(k^2 * 判定常数)

【数据】
20 20 4
2 4 1 3
10 15
11 19
18 14
20 17

*/


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值