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 (pi−pj)⋅(ci−cj)>0 for all 1≤i<j≤4 .
3. no other submatrices inside the submatrix meet the above two conditions.
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 (pi−pj)⋅(ci−cj)>0 for all 1≤i<j≤4 .
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 (1≤n,m,k≤2000) -- 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 (1≤ri≤n,1≤ci≤m) -- the position of the i -th 1. No two 1s will be in the same position.
The first line contains three integers n , m and k (1≤n,m,k≤2000) -- 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 (1≤ri≤n,1≤ci≤m) -- 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
*/