codeforces1195E 2100分单调队列

题目传送门

题意:

一个 \dpi{150}n*m 的矩阵,你有两个数 a 和 b 。

f(i,j) 表示以 (i,j) 为左上角大小为 a*b 的子矩阵里面的数的最小值。 

让你计算 \sum_{i=1}^{n-a+1} \sum_{j=1}^{m-b+1} f(i,j) 。

题解:

c[i][j] 表示以 (i,j) 为左上角大小为 1*b 的子矩阵里面的数的最小值。 

然后这个其实就等价于求连续 b 个数的最小值。可以想到是优先队列。

然后对于 c 这个矩阵的每列都用若干次优先队列,累加即可。

感受:

本来用线段树搞,T了。

然后想了想,发现是单调队列。

代码:

#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 3005 ;
const int inf = 0x3f3f3f3f ;
int n , m , a , b ;
int g0 , x , y , z ;
int g[maxn][maxn] ;
int c[maxn][maxn] ;
int q[maxn] ; 
int main()
{
	scanf("%d%d%d%d" , &n , &m , &a , &b) ;
	scanf("%d%d%d%d" , &g0 , &x , &y , &z) ;
	int temp = g0 ;
	for(int i = 1 ; i <= n ; i ++)
	  for(int j = 1 ; j <= m ; j ++)
	    g[i][j] = temp , temp = int((ll(temp) * x + y) % z) ;
	for(int i = 1 ; i <= n ; i ++)
	{
	   int l = 1 , r = 0 ;
	   for(int j = 1 ; j <= b ; j ++)
	   {
	   	  while(l <= r && g[i][q[r]] > g[i][j])  r -- ;
	   	  q[++ r] = j ;
	   }
	   c[i][1] = g[i][q[l]] ;
	   for(int j = b + 1 ; j <= m ; j ++)
	   {
	   	  while(l <= r && q[l] < j - b + 1) l ++ ;
	   	  while(l <= r && g[i][q[r]] > g[i][j])  r -- ;
	   	  q[++ r] = j ;
	   	  c[i][j - b + 1] = g[i][q[l]] ;
	   }
	}
	ll ans = 0 ;
	for(int j = 1 ; j + b - 1 <= m ; j ++)
	{
	   int l = 1 , r = 0 ;
	   for(int i = 1 ; i <= a ; i ++)
	   {
	   	  while(l <= r && c[q[r]][j] > c[i][j])  r -- ;
	   	  q[++ r] = i ;
	   }
	   ans += c[q[l]][j] ;	   
	   for(int i = a + 1 ; i <= n ; i ++)
	   {
	   	  while(l <= r && q[l] < i - a + 1) l ++ ;
	   	  while(l <= r && c[q[r]][j] > c[i][j])  r -- ;
	   	  q[++ r] = i ;
	   	  ans += c[q[l]][j] ;
	   }
	}
	printf("%lld\n" , ans) ;	  
	return 0 ;
}
发布了216 篇原创文章 · 获赞 12 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Irving0323/article/details/104319154