ACM-ICPC 2018 南京赛区网络预赛 B-The writing on the wall 暴力or经典单调栈

Feeling hungry, a cute hamster decides to order some take-away food (like fried chicken for only 303030 Yuan).

However, his owner CXY thinks that take-away food is unhealthy and expensive. So she demands her hamster to fulfill a mission before ordering the take-away food. Then she brings the hamster to a wall.

The wall is covered by square ceramic tiles, which can be regarded as a n∗mn * mn∗m grid. CXY wants her hamster to calculate the number of rectangles composed of these tiles.

For example, the following 3∗33 * 33∗3 wall contains 363636 rectangles:

Such problem is quite easy for little hamster to solve, and he quickly manages to get the answer.

Seeing this, the evil girl CXY picks up a brush and paint some tiles into black, claiming that only those rectangles which don't contain any black tiles are valid and the poor hamster should only calculate the number of the valid rectangles. Now the hamster feels the problem is too difficult for him to solve, so he decides to turn to your help. Please help this little hamster solve the problem so that he can enjoy his favorite fried chicken.

Input

There are multiple test cases in the input data.

The first line contains a integer TTT : number of test cases. T≤5T \le 5T≤5.

For each test case, the first line contains 333 integers n,m,kn , m , kn,m,k , denoting that the wall is a n×mn \times mn×m grid, and the number of the black tiles is kkk.

For the next kkk lines, each line contains 222 integers: x yx\ yx y ,denoting a black tile is on the xxx-th row and yyy-th column. It's guaranteed that all the positions of the black tiles are distinct.

For all the test cases,

1≤n≤105,1≤m≤1001 \le n \le 10^5,1\le m \le 1001≤n≤105,1≤m≤100,

0≤k≤105,1≤x≤n,1≤y≤m0 \le k \le 10^5 , 1 \le x \le n, 1 \le y \le m0≤k≤105,1≤x≤n,1≤y≤m.

It's guaranteed that at most 222 test cases satisfy that n≥20000n \ge 20000n≥20000.

Output

For each test case, print "Case #xxx: ansansans" (without quotes) in a single line, where xxx is the test case number and ansansans is the answer for this test case.

Hint

The second test case looks as follows:

样例输入

2
3 3 0
3 3 1
2 2

样例输出

Case #1: 36
Case #2: 20

题意:

       给你一个n*m(1e5*100)的矩阵和k(1e5)个黑点,问有多少个不含这些黑点的子矩形。

 

做法:

       一   暴力

        发现n*m*m的暴力做法也能过的时候我是崩溃的,先讲一下暴力的吧,以每一个点为右下角的点去for一次该行,用一个last数组先去记录这一列离当前行最近的那一行的黑点所在,for的时候记录下最大值(最大值不会超过该行i),然后每次都加上(i-maxn),即maxn那么多的行已经被吃掉了不能形成矩阵了。

 (我的习惯是以(1,1)开始)

       以左图为例,当我们for到(4,4),往左边for时,maxn会再第3列等于3,之后的a[i]都是1 2,那么maxn会一直等于3,所以每次加进答案都会是1,然后就是暴力for每个方块的问题了。时间比较久。

 

 

 

 

    二  单调栈维护

       讲起来可能会复杂,先介绍几个变量,last[j],表示到该列的时候上数有多少个合法的方块,如上图,for到(6,3)时,last[3]=1,(当遇到不合法的时候last该列就会清空),,每次当我们for到一个点的时候,我们都会先尝试在栈中加入一个1*last[j]的方块。

       然后我们会先访问当前在栈顶的方块的长(即last),如果在栈顶的长要大于当前的长,那么我们就要把这个方块从res中减掉,res又是什么呢,是当前的方块对下一个方块的影响,如上图,我们在for完(2,3)这个点之后,栈中会有一个1*2的方块,res+=1*2,在for(2,4)这个点的时候,我们会把这个方块也加在res里,然后ans+=res,为什么呢,因为高度是合法的,所以在(2,4)作为右下角的时候,(2,3)和(1,3)都可以作为右上角,所以这个2对(2,4)这个点是有影响的,所以不会减掉,会一直加。(注意,当遇到不合法的方块的时候,res和栈都会清空,重新开始,同时每次for一个新的行的时候也会清空);

       言归正传,假设栈顶的方块的长不合法了,那么我们在减掉这个方块的面积的同时,要把这个方块的宽加上,(因为相同一行的影响还在),这样for到上图的(4,2)这个点的时候,就会加入一个2*2的方便,而这个方块对之后的(4,3),会产生一个宽度为2的影响(因为长会被减掉)。

       如果看懂了就好。。看不懂的话。。。emmm我的语言表达能力也很有限。。看着代码模吧。。


暴力代码如下:

      1454ms

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=100005;
int mp[maxn][105],up[105],n,m,k;
ll ans;
int main(){
    int t,cas=0;
    cin>>t;
    while(t--){
        ans=0;
        memset(up,0,sizeof(up));
        memset(mp,0,sizeof(mp));
        scanf("%d%d%d",&n,&m,&k);
        for(int i=0,x,y;i<k;i++){
            scanf("%d%d",&x,&y);
            mp[x][y]=1;
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(mp[i][j])
                    up[j]=i;
            }
            for(int j=1;j<=m;j++){
                int mmax=0;
                for(int k=j;k>0;k--){
                    mmax=max(mmax,up[k]);
                    ans+=(ll)i-mmax;
                }
            }
        }
        printf("Case #%d: %lld\n",++cas,ans);
    }
    return 0;
}

正经代码如下:

       187ms(快得多了)

#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int maxn=100005;
pii Stack[105];
int n,m,k,mark[105],top,last[105],now[105];
vector<int> ve[maxn];
ll ans,res;
void Clear(){
    top=0,res=0;
}
int main(){
    int t,cas=0;
    cin>>t;
    while(t--){
        memset(last,0,sizeof(last));
        scanf("%d%d%d",&n,&m,&k);
        for(int i=0,x,y;i<k;i++){
            scanf("%d%d",&x,&y);
            ve[x].push_back(y);
        }
        ans=0;
        for(int i=1;i<=n;i++){
            for(int j=0;j<ve[i].size();j++)
                mark[ve[i][j]]=1;
            Clear();
            for(int j=1;j<=m;j++){
                int x=1;
                if(!mark[j]) now[j]=last[j]+1;
                else now[j]=0;
                last[j]=now[j];
                if(!last[j]) Clear();
                else {
                    int y=last[j];
                    while(top&&Stack[top].se>y){
                        x+=Stack[top].fi;
                        res-=Stack[top].se*Stack[top].fi;
                        top--;
                    }
                    Stack[++top]={x,y};
                    res+=x*y;
                    ans+=res;
                }
            }

            for(int j=0;j<ve[i].size();j++)
                mark[ve[i][j]]=0;
            ve[i].clear();
        }
        printf("Case #%d: %lld\n",++cas,ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值