洛谷 P3397 地毯 【二维差分标记】
题目背景
此题约为NOIP提高组Day2T1难度。
题目描述
在n*n的格子上有m个地毯。
给出这些地毯的信息,问每个点被多少个地毯覆盖。
输入输出格式
输入格式:
第一行,两个正整数n、m。意义如题所述。
接下来m行,每行两个坐标(x1,y1)和(x2,y2),代表一块地毯,左上角是(x1,y1),右下角是(x2,y2)。
输出格式:
输出n行,每行n个正整数。
第i行第j列的正整数表示(i,j)这个格子被多少个地毯覆盖。
输入输出样例
输入样例#1:
5 3
2 2 3 3
3 3 5 5
1 2 1 4
输出样例#1:
0 1 1 1 0
0 1 1 0 0
0 1 2 1 1
0 0 1 1 1
0 0 1 1 1
说明
样例解释
0 0 0 0 0 0 0 0 0 0 0 1 1 1 0
0 1 1 0 0 0 1 1 0 0 0 1 1 0 0
0 1 1 0 0 -> 0 1 2 1 1 -> 0 1 2 1 1
0 0 0 0 0 0 0 1 1 1 0 0 1 1 1
0 0 0 0 0 0 0 1 1 1 0 0 1 1 1
数据范围
对于20%的数据,有n<=50,m<=100。
对于100%的数据,有n<=1000,m<=1000。
【題意】:略
【分析】://by阮行止
n*n的矩阵,每次给一个子矩阵 区间+1 。最后输出整个矩阵。
优化。用二维线段树或二维树状数组完成上面的操作。
足以吊打此题。
但是NOIP是不会考二维数据结构的
考虑这个问题的一维版:一个序列,最开始全是 0 .每次区间加 1 ,最后输出每个数。
于是有一种叫做“差分”的奇技淫巧:
假设我们现在要给[2,5]这个区间加一。原来的序列是:
0 0 0 0 0 0 0 0
这时候我们在2上面打 +1 标记, 6 上面打 -1 标记。那么现在的序列是:
0 +1 0 0 0 -1 0
有什么用呢?从左往右扫描这个数组,记录当前经过的标签之和。这个和就是对应那个数的答案。
这样,对于每个区间加操作,只需要O(1) 的时间打上标记。
最后扫描输出即可。
现在把问题拓展到二维。假设我们要覆盖[(2,2),(5,5)] ,那么标记便可以这样打:
0 0 0 0 0 0
0 +1 0 0 0 -1
0 +1 0 0 0 -1
0 +1 0 0 0 -1
0 +1 0 0 0 -1
0 0 0 0 0 0
即在每一行都按照一维的方式来操作
int sum=0,i,j;
for(i=1;i<=n+1;i++)
for(j=1;j<=n+1;j++)
sum+=flag[i][j],real[i][j]=sum;
之后 real 数组里就存了最后的矩阵。输出即可。
这个算法的复杂度是每次打标记O(n) ,总复杂度是O(mn+n^2)
【代碼】:[一維]
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
//#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[1005][1005];
int x[1005][1005];
const int maxn=1e5+10;
void fun(int x1,int y1,int x2, int y2)
{
for(int i=x1; i<=x2; i++)
{
for(int j=y1; j<=y2; j++)
{
a[i][j]++;
}
}
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
int x1,y1,x2,y2;
memset(a,0,sizeof(a));
while(m--)
{
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
for(int i=x1;i<=x2;i++)
{
a[i][y1]++;
a[i][y2+1]--;
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
x[i][j] = x[i][j-1] + a[i][j];
printf("%d ",x[i][j]);
}
printf("\n");
}
}
}
/*
5 3
2 2 3 3
3 3 5 5
1 2 1 4
0 1 1 1 0
0 1 1 0 0
0 1 2 1 1
0 0 1 1 1
0 0 1 1 1
0 0 0 0 0 0 0 0 0 0 0 1 1 1 0
0 1 1 0 0 0 1 1 0 0 0 1 1 0 0
0 1 1 0 0 -> 0 1 2 1 1 -> 0 1 2 1 1
0 0 0 0 0 0 0 1 1 1 0 0 1 1 1
0 0 0 0 0 0 0 1 1 1 0 0 1 1 1
*/
首先这题的主要思想很多大佬都讲了:就是差分,但是我的写法和他们的写法又不一样。
本题数据范围为n<=1000,m<=1000。
当n<=1000,m<=1000000甚至m<=10000000怎么办呢?
这时不管是O(n)的修改,还是甚至是 O(log^2(n))O(log
2
(n)) ,都是跑不过的。
而我们有一个O(1)修改的做法:二维差分。
设b[i][j]=a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1]。
这样每次修改b[i][j]相当于对任意 i\le x,j \le yi≤x,j≤y 对a[x][y]做同样的修改
然后每次修改就直接++b[x1][y1],--b[x2+1][y1],--b[x1][y2+1],++b[x2+1][y2+1]即可。
也就是用 O(1)O(1) 的复杂度表示 O(n^2)O(n
2
) 的覆盖(原话来自下面那篇题解“用O(1)复杂度来表示O(N)的覆盖”)
最后再直接a[i][j]=a[i-1][j]+a[i][j-1]-a[i-1][j-1]+b[i][j]还原出原序列即可。
/* Author: CNYALI_LK
LANG: C++
PROG: 3397.cpp
*/
[二維]
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
//#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[1005][1005];
int x[1005][1005];
const int maxn=1e5+10;
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
int x1,y1,x2,y2;
memset(a,0,sizeof(a));
while(m--)
{
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
a[x1][y1]++; //
a[x2+1][y1]--;
a[x1][y2+1]--; //
a[x2+1][y2+1]++;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
a[i][j] += a[i-1][j] + a[i][j-1] - a[i-1][j-1];
printf("%d%c",a[i][j],j==n?'\n':' ');
}
}
}
}
我们借助这个图片研究一下。假设在这个矩阵(二维数组)中,我们要求和的是上图中红色区域。现在我们已经预处理出了所有点的前缀和,现在给定两个点(x1,y1),(x2,y2),我们要求 以这两个点连线为对角线的一个子矩阵的数值之和。暴力做法直接挨个加这个我就不再多说了,反正早晚都得TLE,我们重点考虑用前缀和的快速做法。
首先我们可以把s[x2][y2]求出来,它代表整个大矩形的前缀和,然后我们分别减去它左边多出来的一块的前缀和和下边多出来一块的前缀和,这样就是最终答案了?
不是!这不是最终答案。可以发现,在我们剪掉这两个多出的区域时,下边的一小块被减了两次,但减两次显然是不合理的,我们应该加回来。。
所以对于一次的查询答案ans应该等于s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1]。
这个二维前缀和也称差分序列。
洛谷 P3397 地毯 【二维差分标记】的更多相关文章
- 洛谷 P3397 地毯
P3397 地毯 题目背景 此题约为NOIP提高组Day2T1难度. 题目描述 在n*n的格子上有m个地毯. 给出这些地毯的信息,问每个点被多少个地毯覆盖. 输入输出格式 输入格式: 第一行,两个正整 ...
- 洛谷1736(二维dp+预处理)
洛谷1387的进阶版,但很像. 1387要求是“全为1的正方形”,取dp[i][j] = min(dp[i-1][j-1], min(dp[i-1][j], dp[i][j-1]))吧?这个有“只有对 ...
- 洛谷P1048 采药 二维dp化一维
题目描述 辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师.为此,他想拜附近最有威望的医师为师.医师为了判断他的资质,给他出了一个难题.医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个 ...
- 洛谷P3397 地毯(差分)
二维平面上的差分,我们可以对每行处理. 比如我们要把(2,2)(5,5)之间的矩形加上1,可以这样处理. 0 0 0 0 0 0 0 +1 0 0 0 -1 0 +1 0 0 0 -1 0 +1 0 ...
- Codeforces 1262E Arson In Berland Forest(二维前缀和+二维差分+二分)
题意是需要求最大的扩散时间,最后输出的是一开始的火源点,那么我们比较容易想到的是二分找最大值,但是我们在这满足这样的点的时候可以发现,在当前扩散时间k下,以这个点为中心的(2k+1)2的正方形块内必 ...
- Monitor HDU6514 二维差分入门学习
Monitor HDU 6514 二维差分入门学习 题意 小腾有\(n*m\)的田地,但是有小偷来偷东西,在一片矩形区域上,有一部分区域是监控可以覆盖到的,这部分区域由一个或多个包含于该矩形区域的小矩 ...
- Gym 102028J 扫描线/二维差分 + 解方程
题意:有一个二维平面,以及n个操作,每个操作会选择一个矩形,使得这个二维平面的一部分被覆盖.现在你可以取消其中的2个操作,问最少有多少块地方会被覆盖? 思路:官方题解简洁明了,就不细说了:https: ...
- 220514 T2 画画 (二维差分)
首先我们需要特判只涂了一种颜色的情况: (1)k=1,此时答案就是1:(2)k>1,涂的这种颜色肯定不能是第一个,答案是k-1; 对于其他正常情况,我们对于每个颜色找到一个最小的矩形(这个矩形内 ...
- HDU - 6514 Monitor(二维差分)
题意 给定一个\(n×m\)的矩阵.(\(n×m <= 1e7\)). \(p\)次操作,每次可以在这个矩阵中覆盖一个矩形. \(q\)次询问,每次问一个矩形区域中,是否所有的点都被覆盖. 解析 ...
随机推荐
- 一道js的前端面试题,主要弄清楚逗号表达式的作用
群里看到的一道前端JS面试题.我以为我会,其实我错了.找了很多资料,写下来. var i,j,k; for( i = 0 , j = 0; i < 10 , j < 6; i++ , j+ ...
- Codeforces Round #519 by Botan Investments翻车记
A:枚举答案即可.注意答案最大可达201,因为这个wa了一发瞬间爆炸. #include<iostream> #include<cstdio> #include<cmat ...
- HDU 4776 Ants(Trie+优先队列)
Ants Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 327680/327680 K (Java/Others) Total S ...
- [51nod1503]猪和回文 DP
---题面--- 题解: 首先观察到题目要求的是合法回文串的个数,而回文串要求从前往后和从后往前是一样的,因此我们假设有两只猪,分别从左上和右下开始走,走相同的步数最后相遇,那么它们走的路能拼在一起构 ...
- LinuxUnix time时间戳的处理转换函数
Linux/Unix time时间戳的处理转换函数 linux下的时间函数 我们在编程中可能会经常用到时间,比如取得系统的时间(获取系统的年.月.日.时.分.秒,星期等),或者是隔一段时间去做某事,那 ...
- codevs 1078 最小生成树 kruskal
题目描述 Description 农民约翰被选为他们镇的镇长!他其中一个竞选承诺就是在镇上建立起互联网,并连接到所有的农场.当然,他需要你的帮助. 约翰已经给他的农场安排了一条高速的网络线路,他想把这 ...
- 安卓下拉刷新空间SwipeRefreshLayout的基本使用
1.先写布局文件 <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/refresh" androi ...
- HTTP协议中的ContenType类型大全(转)
".*"="application/octet-stream"".001"="application/x-001"&qu ...
- JS向右弹出DIV,点击可向左隐藏。我用jquery可以从左下角像右上角隐藏,怎么从做向右隐藏呢?
弹出的DIV如果是绝对定位,就用right固定位子,如果不是就用float:right:Jquery中有个函数animate是自定义动画效果,$("#shou").click(fu ...
- 【sgu390】数位dp
这题其实就是不断地合并子树,跟前面例一的思想是一样的. 这个打法我觉得非常优美啊(学别人的),为什么要搞lim1和lim2呢? 是因为在区间lim1~lim2之外的都是没有用的,但是我们f[h][su ...