[Codeforces Round #296]Glass Carving

Description

Leonid wants to become a glass carver (the person who creates beautiful artworks by cutting the glass). He already has a rectangular w mm  ×  h mm sheet of glass, a diamond glass cutter and lots of enthusiasm. What he lacks is understanding of what to carve and how.

In order not to waste time, he decided to practice the technique of carving. To do this, he makes vertical and horizontal cuts through the entire sheet. This process results in making smaller rectangular fragments of glass. Leonid does not move the newly made glass fragments. In particular, a cut divides each fragment of glass that it goes through into smaller fragments.

After each cut Leonid tries to determine what area the largest of the currently available glass fragments has. Since there appear more and more fragments, this question takes him more and more time and distracts him from the fascinating process.

Leonid offers to divide the labor — he will cut glass, and you will calculate the area of the maximum fragment after each cut. Do you agree?

Input

The first line contains three integers w, h, n (2 ≤ w, h ≤ 200 000, 1 ≤ n ≤ 200 000).

Next n lines contain the descriptions of the cuts. Each description has the form H y or V x. In the first case Leonid makes the horizontal cut at the distance y millimeters (1 ≤ y ≤ h - 1) from the lower edge of the original sheet of glass. In the second case Leonid makes a vertical cut at distance x (1 ≤ x ≤ w - 1) millimeters from the left edge of the original sheet of glass. It is guaranteed that Leonid won’t make two identical cuts.

Output

After each cut print on a single line the area of the maximum available glass fragment in mm2.

Sample test(s)

input
4 3 4
H 2
V 2
V 3
V 1

output
8
4
4
2

input
7 6 5
H 4
V 3
V 5
H 2
V 1

output
28
16
12
6
4

Note

Picture for the first sample test:
这里写图片描述
Picture for the second sample test:
这里写图片描述

Stantard Report

Obviously the largest glass piece at any moment is the one that is product of the largest horizontal segment by the largest vertical segment. One of the possible solutions is to carefully implement what described in the statement and keep all horizontal segments and all vertical segments in priority queue or std::set, or some logarithmic data structure. This solution works in .

But there is also a nice linear solution if we answer all queries in reverse order. Suppose segments are not cutting, but merging. In this case we may keep the horizontal and vertical cut lines in double-linked lists and track the current maximum (that can only increase and become equal to the newly-merged segment each time). This solution works in O(k + n + m).

My Problem Report

这道题要求每次切割过后剩下最大的玻璃块面积。这个问题可以转化为求每次切割后x轴上最长线段和y轴上最长险段。
最开始拿到这道题我想的是用两个堆和两个队列来维护,将当前先生与线段放入堆,将需要处理的命令放入队列。每次当队头元素在堆顶区间内时,才将队头弹出。后来再一想,这样的方法是大错特错的。因为队头元素不是每次都是我们所需要的,有可能某一次堆顶元素的区间需要被切割时,切割操作还没有到达队头。
于是我开始重新审视这个问题。当进行每次切割时和查询,实际上我们只执行了三步操作:查找特定区间,插入,查找最大区间。这个时候我发现堆和队列都是不能实现特定区间(值)查找的,并且我们所围护的这一系列特定区间(值)不具有单调性,因此二分查找也不可行。
在logn时间内查找指定值,修改后重新插入,搜索二叉树恰好满足这样的要求。我们考虑用两个set来维护切割点,两个set来维护线段。因为set内部采用的是中序遍历,因此当我们插入一个切割点之后,该点前一个指针与后一个指针恰好与它相邻,于是我们可以很方便地计算出需要删除和添加的线段长度。

Code

//  Created by Chlerry in 2015.
//  Copyright (c) 2015 Chlerry. All rights reserved.
//  http://codeforces.com/problemset/problem/527/C

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <cmath>
#include <map>
#include <sstream>
using namespace std;

multiset<long long> a[2];
multiset<long long> ha[2];
multiset<long long>::iterator it,i0,i1;

void Output();

int main()
{
    //freopen("in.txt","r",stdin);
    long long w,h,temp,l,r;
    int n;
    char ch[2];

    cin>>w>>h>>n;
    a[0].insert(0);a[0].insert(w);ha[0].insert(w);
    a[1].insert(0);a[1].insert(h);ha[1].insert(h);
    while(n--)
    {
        cin>>ch>>temp;
        bool flag=(ch[0]=='H');
        //cout<<ch<<' '<<temp<<endl;
        a[flag].insert(temp);
        it=a[flag].find(temp);
        l=temp-*(--it);
        r=*(++++it)-temp;

        it=ha[flag].find(l+r);
        ha[flag].erase(it);//Wrong ha[flag].erase(l+r);
        ha[flag].insert(l);
        ha[flag].insert(r);

        i0=ha[0].end();
        i1=ha[1].end();
        //Output();
        cout<<(*(--i0))*(*(--i1))<<endl;
    }
    return 0;
}
void Output()
{
    cout<<"W: ";
    for(it=ha[0].begin();it!=ha[0].end();it++)
        cout<<*it<<" ";
    cout<<endl;
    cout<<"V: ";
    for(it=ha[1].begin();it!=ha[1].end();it++)
        cout<<*it<<" ";
    cout<<endl;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值