hdu 5749 公式

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5749


参考文章:http://m.blog.csdn.net/article/details?id=52020617

题目要求求出这个公式[图片]的值,首先来看S(a,b),其实对于一个矩阵,可以手动画一下,一个矩阵的鞍点至多只有一个,那么s(a,b)其实就是矩阵中这个鞍点的值,那么如何来求鞍点。

如果一个点能成为某个矩阵的鞍点,按题目所说,那么这个点的左右的点都比这个点大,上下的点都比这个点小,那么对于一个点就是要求出最长这个点可以把四个边延长多长,u,d,l,r。用单调栈的处理可以快一点,不知道暴力处理能不能过。

处理出来好后,因为一个矩阵最多只有一个这样的鞍点,那么,就是枚举每个点,它能成为多少个矩阵的鞍点,因为已经知道一个点作为鞍点所能构成的最大矩阵,那么在这个矩阵内部且包含该点的都是满足条件的矩阵。然后原来的公式呢其实就是枚举每一个这样的矩阵加起来。具体化解可以看这里http://m.blog.csdn.net/article/details?id=52020617

#include<iostream>
#include<cstdio>
#include<map>
#include<cstring>
#include<string>
#include<stack>
#include<queue>
#include<algorithm>
#include<cmath>
#include<vector>

using namespace std;
#define LL long long
LL u[1005][1005];
LL d[1005][1005];
LL l[1005][1005];
LL r[1005][1005];
LL dat[1005][1005];
int n,m;
void Left()
{
	for(int i = 0;i<n;i++)
	{
		stack<pair<int,int> >sta;
		for(int j = 0;j<m;j++)
		{
			if(sta.empty())
			{
				pair<int,int> temp;
				temp.first = dat[i][j];
				temp.second = j;
				sta.push(temp);
				l[i][j] = 1;
			}
			else
			{
				while(!sta.empty() && sta.top().first>dat[i][j])
				{
					sta.pop();
				}
				if(sta.empty())
				{
					l[i][j] = j+1;
				}
				else
				l[i][j] = j-sta.top().second;
				pair<int,int>temp;
				temp.first = dat[i][j];
				temp.second = j;
				sta.push(temp);
			}
		}
	}
}
void Right()
{
	for(int i = 0;i<n;i++)
	{
		stack<pair<int,int> >sta;
		for(int j = m-1;j>=0;j--)
		{
			if(sta.empty())
			{
				pair<int,int> temp;
				temp.first = dat[i][j];
				temp.second = j;
				sta.push(temp);
				r[i][j] = 1;
			}
			else
			{
				while(!sta.empty() && sta.top().first>dat[i][j] )
				{
					sta.pop();
				}
				pair<int,int>temp;
				if(sta.empty())
				r[i][j] = m-j;
				else
				r[i][j] = sta.top().second-j;
				temp.first = dat[i][j];
				temp.second = j;
				sta.push(temp);
			}
		}
	}
}
void UP()
{
	for(int j = 0;j<m;j++)
	{
		stack<pair<int,int> >sta;
		for(int i = 0;i<n;i++)
		{
			if(sta.empty())
			{
				pair<int,int> temp;
				temp.first = dat[i][j];
				temp.second = i;
				sta.push(temp);
				u[i][j] = 1;
			}
			else
			{
				//cout << "666";
				while(!sta.empty() && sta.top().first<dat[i][j] )
				{
					sta.pop();
				}
				if(sta.empty())
				{
					u[i][j] = i+1;
				}
				else
				u[i][j] = i-sta.top().second;
				pair<int ,int> temp;
				temp.first = dat[i][j];
				temp.second = i;
				sta.push(temp);
			}
		}
	}
}
void Down()
{
	for(int j = 0;j<m;j++)
	{
		stack<pair<int,int> >sta;
		for(int i = n-1;i>=0;i--)
		{
			if(sta.empty())
			{
				pair<int,int> temp;
				temp.first = dat[i][j];
				temp.second = i;
				sta.push(temp);
				d[i][j] = 1;
			}
			else
			{
				while(!sta.empty() && sta.top().first<dat[i][j])
				{
					sta.pop();
				}
				if(sta.empty())
				{
					d[i][j] = n-i;
				}
				else
				d[i][j] = sta.top().second-i;
				pair<int ,int> temp;
				temp.first = dat[i][j];
				temp.second = i;
				sta.push(temp);
			}
		}
	}
}
void Print(LL arr[][1005])
{
	for(int i = 0;i<n;i++)
	{
		for(int j = 0;j<m;j++)
		{
			printf("%4lld",arr[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		
		scanf("%d%d",&n,&m);
		for(int i = 0;i<n;i++)
		{
			for(int j = 0;j<m;j++)
			{
				scanf("%lld",&dat[i][j]);
			}
		}
		// 左边界 
		
		Left();
		Right();//printf("123");
		UP();
		Down();
		
		LL ans = 0;
		for(int i = 0;i<n;i++)
		{
			for(int j = 0;j<m;j++)
			{
				LL temp = (l[i][j]*r[i][j]*u[i][j]*d[i][j])*(l[i][j]+r[i][j])*(u[i][j]+d[i][j]);
				temp *= dat[i][j];
				temp /= 4;
				temp %= (1ll<<32);
				ans += temp;
				ans %= (1ll<<32);
			}
		}
		printf("%lld\n",ans);
	} 
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值