• [BZOJ 2738] 矩阵乘法 【分块】


    题目链接:BZOJ - 2738

    题目分析

    题目名称 “矩阵乘法” 与题目内容没有任何关系..就像VFK的 A+B Problem 一样..

    题目大意是给定一个矩阵,有许多询问,每次询问一个子矩阵中的第 k 小值。

    我看了神犇的题解,使用一种非常神奇的做法:

    将矩阵中的数排个序,从小到大填到矩阵中。每次填 Size 个(这里就是分块)。

    然后每填完一次,就暴力重新求一下 Sum[][] (二维前缀和), 然后枚举每个询问,看看这个询问的子矩形内已经填入的数是否不少于询问的 k 。

    如果子矩形内已填入的数不少于询问的 k ,那么这个询问的答案一定就是在这一次填入的 Size 个数中,于是就枚举这 Size 个数,然后判断每个数是不是在子矩形内,就可以求出刚好填到第 k 个是哪个数了。

    然后就可以将这次询问删掉了,用双向链表实现删除一个询问。

    复杂度是 O(n^4 / Size + n^2 * q / Size + q * Size) ,然后用均值不等式求出一个最优的 Size 就可以了。

    代码

    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    
    using namespace std;
    
    typedef double LF;
    
    inline int gmin(int a, int b) {return a < b ? a : b;}
    
    const int MaxN = 500 + 5, MaxM = 60000 + 5;
    
    int n, m, Top, Blk, S, T;
    int Sum[MaxN][MaxN], Has[MaxN][MaxN];
    
    struct ENum
    {
    	int Num, x, y;
    } E[MaxN * MaxN];
    
    inline bool Cmp(ENum e1, ENum e2)
    {
    	return e1.Num < e2.Num;
    }
    
    struct EQ
    {
    	int x, y, xx, yy, k, Ans, Prev, Next;
    	
    	bool Inside(ENum e)
    	{
    		return (e.x >= x && e.x <= xx) && (e.y >= y && e.y <= yy);
    	}
    } Q[MaxM];
    
    inline int GetSum(int x, int y, int xx, int yy)
    {
    	return Sum[xx][yy] - Sum[x - 1][yy] - Sum[xx][y - 1] + Sum[x - 1][y - 1];
    }
    
    int main()
    {
    	scanf("%d%d", &n, &m);
    	Blk = (int)((LF)n * (1.0 + ((LF)n / sqrt((LF)m)))) + 1;
    	Top = 0;
    	for (int i = 1; i <= n; ++i)
    		for (int j = 1; j <= n; ++j)
    		{
    			scanf("%d", &E[++Top].Num);
    			E[Top].x = i; E[Top].y = j;
    		}
    	sort(E + 1, E + Top + 1, Cmp);	
    	for (int i = 1; i <= m; ++i)
    		scanf("%d%d%d%d%d", &Q[i].x, &Q[i].y, &Q[i].xx, &Q[i].yy, &Q[i].k);
    	for (int i = 1; i < m; ++i) Q[i].Next = i + 1;
    	for (int i = 2; i <= m; ++i) Q[i].Prev = i - 1;
    	S = m + 1; T = m + 2;
    	Q[S].Next = 1; Q[1].Prev = S;
    	Q[m].Next = T; Q[T].Prev = m;
    	int p;
    	for (int i = 1; i <= Top; i += Blk)
    	{
    		p = gmin(i + Blk - 1, Top);
    		for (int j = i; j <= p; ++j)
    			Has[E[j].x][E[j].y] = 1;
    		for (int j = 1; j <= n; ++j)
    			for (int k = 1; k <= n; ++k)
    				Sum[j][k] = Sum[j - 1][k] + Sum[j][k - 1] - Sum[j - 1][k - 1] + Has[j][k];
    		int j = Q[S].Next;
    		while (j != T)
    		{
    			int CntNow = GetSum(Q[j].x, Q[j].y, Q[j].xx, Q[j].yy);
    			if (CntNow >= Q[j].k)
    			{
    				int Pos = p;
    				while (CntNow >= Q[j].k)
    				{
    					if (Q[j].Inside(E[Pos])) --CntNow;
    					--Pos;
    				}
    				Q[j].Ans = E[Pos + 1].Num;
    				Q[Q[j].Next].Prev = Q[j].Prev;
    				Q[Q[j].Prev].Next = Q[j].Next;		
    			}
    			j = Q[j].Next;
    		}
    	}
    	for (int i = 1; i <= m; ++i) printf("%d
    ", Q[i].Ans);
    	return 0;
    }
    

      

  • 相关阅读:
    简单了解winform
    SqL语句基础之增删改查
    数据库之表
    数据库基本概念与操作
    搞死人不偿命的 Bank系统
    for的循环题
    .net framework 版本汇总
    LinqToEntity模糊查询的方法选择
    日常工作中的点滴:C# 根据字节长度截包含中文的字符串
    64位系统 IIS中应用程序池设置导致 访问数据库错误
  • 原文地址:https://www.cnblogs.com/JoeFan/p/4554984.html
Copyright © 2020-2023  润新知