DoIt is Being Flooded

Description

Global warming affects the whole world right now. DoIt is a country which is surrounded by the sea and suffering from global warming. Scientists in DoIt find out that the sea level will rise 1 meter every year so that some places in DoIt will be flooded and disappear from the earth. As a result, the country may be divided into several islands and finally disappear.

DoIt's government wants to know how much it can earn before the whole country's disappearance. Thanks for economists, its income can be decided by the size of unflooded places and an economic coefficientA. A will change with years and it has been well forecasted by economists. The total income of DoIt is the sum of all islands' income. For each island, its income equals2 Bits(S & A). S is the size of the island and A is the economic coefficient of that year.Bits(x) means the number of 1 in x's binary code.

Assuming DoIt can be represented by n * m grids, an integer in a grid indicates its altitude. The size of a grid is 1. Two grids are adjacent if they have a common edge. Note that, water in a grid can only flow into grids which are adjacent to the current grid. At first, it's year 0 and the sea level is 0. Therefore, in thed-th year, the sea level will rise to d at the very begining of the year. There are several queries asked by the government. Each query includes two integers,d, A, representing the year and the economic coefficient of that year. You should output DoIt's total income in thed-th year. You can assume that the sea level changes at the beginning of the year and keeps unchanged in that year. So the total income is caclulated after this change.

Input

There are multiple test cases (about 10).

For each case, in the first line there are two integers n and m (1 ≤n, m ≤ 500). Then in the next n lines, each line has m integers. It's guaranteed that all altitudes are larger than 0 and no more than 1000000.

After that, there is an integer Q (1 ≤ Q ≤ 10000), representing the number of queries. In the nextQ lines, each line has two integers, d (1 ≤ d ≤ 1000000) andA (0 ≤ A ≤ 1000000000). It's possible that different economic coefficients are found in the same year.

Output

For each query, output the total income of DoIt in that year.

Sample Input

5 5
6 6 6 6 8
5 2 2 2 5
5 2 3 2 4
8 2 1 1 5
7 4 5 5 5
4
3 255
5 255
7 255
9 255

Sample Output

8
6
4
0
Hint

Because of the large input, scanf is recommended.


题意:

一个图,每个点为一个建筑物,在发生洪水的时候有多少建筑是没有被淹的,

注意一点就是洪水只能从相邻的位置流进去,样例当高度为3时,是淹不到

建筑物的,最后的结果就是2^(每个连通块的个数&A的二进制位的个数),

统计和就是答案。

这道题涉及的东西挺多的,需要用优先队列将相邻的值设置成较大(开始只保存边界就行),

只能从边界流进去,所以每次出最小的值能流到的位置然后设置较大的那个值,然后

保存所有的值,这样就可以通过从大到小统计连通块,并统计每个连通块的个数,先

将答案预处理,然后只需要输出就行,保存答案时用数组就行,开始用map内存超限。

#include <stdio.h>
#include <queue>
#include <map>
#include <algorithm>
#include <string.h>
using namespace std;
#define CLR( a ) memset ( a, 0, sizeof ( a ) )
#define LL long long
#define DBUG printf ( "Here!\n" )
const int maxn = 505, M = maxn*maxn, QY = 10005;
const int dx[4] = { 0, 1, -1, 0 }, dy[4] = { 1, 0, 0, -1 };
int mp[maxn][maxn], n, m;
int father[M], num[M], ans[QY];
map < int, int > cur;
bool vis[maxn][maxn];
struct node
{
    int h, x, y;
    friend bool operator < ( node n1, node n2 )
    {//优先队列小的先出,排序就是降序
        return n1.h > n2.h;
    }
} nd[M], t;
struct query
{
    int d, a, i;
    friend bool operator < ( query q1, query q2 )
    {
        return q1.d > q2.d;
    }
}qry[QY];
priority_queue < node > q;
inline int Max ( int a, int b )
{
    return a > b ? a : b;
}
inline bool check ( int x, int y )
{
    return x < 0 || x >= n || y < 0 || y >= m;
}
void init ( )
{
    for ( int i = 0; i <= n*m; i ++ )
    {
        father[i] = i;
        num[i] = 1;
    }
    CLR ( vis );
    cur.clear ( );
}
int find ( int x )
{
    int r = x, i, j;
    while ( r != father[r] )
        r = father[r];
    i = x;
    while ( i != r )
    {
        j = father[i];
        father[i] = r;
        i = j;
    }
    return r;
}
void merge ( int fx, int fy )
{
    father[fy] = fx;
    num[fx] = num[fx]+num[fy];
}
int bit_cnt ( int n )
{
    int ret = 0;
    while ( n > 0 )
    {
        if ( n&1 )
            ret ++;
        n = n >> 1;
    }
    return ret;
}
int main ( )
{
    int cnt, Q;
    while ( ~ scanf ( "%d%d", &n, &m ) )
    {
        cnt = 0;
        CLR ( vis );
        while ( ! q.empty ( ) ) //清空
            q.pop ( );
        for ( int i = 0; i < n; i ++ )
            for ( int j = 0; j < m; j ++ )
            {
                scanf ( "%d", &mp[i][j] );
                if ( i == 0 || j == 0 || i == n-1 || j == m-1 )
                {
                    nd[cnt].x = i, nd[cnt].y = j, nd[cnt].h = mp[i][j];
                    q.push ( nd[cnt] ); //将边界保存
                    cnt ++;
                    vis[i][j] = true;
                }
            }
        while ( ! q.empty ( ) )
        {
            t = q.top ( );
            q.pop ( );
            for ( int i = 0; i < 4; i ++ )
            {   //将最小值相邻设置成两个中比较大的那个
                int nx = t.x+dx[i];
                int ny = t.y+dy[i];
                if ( check ( nx, ny ) || vis[nx][ny] )
                    continue ;
                vis[nx][ny] = true; //标记
                mp[nx][ny] = Max ( mp[nx][ny], mp[t.x][t.y] );
                //设置成两个中间较大的值
                nd[cnt].x = nx, nd[cnt].y = ny, nd[cnt].h = mp[nx][ny];
                q.push ( nd[cnt] );
                cnt ++;
            }
        }
        sort ( nd, nd+cnt );    //降序
        scanf ( "%d", &Q );
        for ( int i = 0; i < Q; i ++ )
        {
            scanf ( "%d%d", &qry[i].d, &qry[i].a );
            qry[i].i = i;   //注意将编号保存,输出的时候就可以直接输出
        }
        sort ( qry, qry+Q );
        //对高度降序,那样就可以直接统计连通块块数和每块的个数
        init ( );
        for ( int i = 0, c = 0; i < Q; i ++ )
        {
            while ( c < cnt && nd[c].h > qry[i].d )
            //统计所有高度大于d的个数,和连通块数
            {
                int x = nd[c].x, y = nd[c].y;
                vis[x][y] = true;   //标记有此数
                cur[1] ++;  //1个的个数加1
                for ( int j = 0; j < 4; j ++ )  //判断连通的个数
                {
                    int nx = x+dx[j];
                    int ny = y+dy[j];
                    if ( check ( nx, ny ) || vis[nx][ny] == false )
                        continue ;
                    int a = x*m+y, b = nx*m+ny;
                    //将二维换成一维的坐标
                    int fa = find ( a ), fb = find ( b );
                    if ( fa != fb )
                    {
                        cur[ num[fa] ] --;  //fa连通块的个数减1
                        if ( cur[ num[fa] ] == 0 )  //为0时删除
                            cur.erase ( num[fa] );
                        cur[ num[fb] ] --;  //fb同理
                        if ( cur[ num[fb] ] == 0 )
                            cur.erase ( num[fb] );
                        merge ( fa, fb );   //合并 将总个数求出
                        cur[ num[fa] ] ++;  //统计连通个数为num[fa]的个数
                    }
                }
                c ++;
            }
            int val = 0;
            for ( map < int, int > :: iterator it = cur.begin ( ); it != cur.end ( ); it ++ )
                val = val+( ( *it ).second << bit_cnt ( ( *it ).first & qry[i].a ) );
            //迭代map,个数*( 1 << bit_cnt ( cnt & A ) )
            ans[ qry[i].i ] = val;  //下标qry[i].i的值为val
        }
        for ( int i = 0; i < Q; i ++ )
            printf ( "%d\n", ans[i] );
    }
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值