[Author : DS]Codeforces 243D 解题报告

time limit per test
5 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

题意:

给一个n * n 的Matrix(n <= 1e3),Aij 代表空地上(i - 1 , j - 1)-(i , j) 顶角的砖块高度。再给一个向量V(Vx , Vy) 代表从无穷远处射过来一束平行光, 问能看到的砖块是多少个。


解法:

一开始看这题木就觉得应该是线段树。

1、线段添加的顺序问题,再枚举遮挡问题即可。很容易想到, 如果点P , Q  , P 在 Q的前方那么必然 P · V < Q · V因此, 枚举四个顶点, 找到点积最小的点, 按照这个与V的点积排序。

2、线段L , R 的问题。直接按照对于V的犄角排序即可,去掉重复的点。因此选用每个Block四个顶点与V叉积最大的和最小的作为R、L即可。

3、线段树,要用“在线段上更新”的线段树。。。也就是[l , l + 1]作为叶子节点的线段树。第一次写。。。维护区间最小值,用区间最大值来更新。

总体来说就是一个离散化+线段树的恶心题目。。。留作木板


Code:

http://codeforces.com/contest/243/submission/2588354

/* .........楼上为岛式头文件.........http://www.shuizilong.com/house/............................. */
struct Point{    // 点类
    int x , y , d;
    Point(){}
    Point(int _x , int _y):x(_x) , y(_y){}
    int operator ^ (const Point & A) const{ // 叉积
        return x * A.y - A.x * y;
    }
    int operator * (const Point & A) const{ // 点积
        return x * A.x + y * A.y;
    }
    Point operator - (const Point & A) const{
        return Point(x - A.x , y - A.y);
    }
    bool operator == (const Point & A) const{
        return x == A.x && y == A.y;
    }
}V;
int n ;
/* 极少的纯手写线段树啊!!!! */
const int NN = (1e6 + 10) * 2;
int tree[NN << 2] , add[NN << 2];
#define lson l , m  , rt << 1
#define rson m , r , rt << 1 | 1
void PushUp(int rt) {   // 维护区间最小值
    tree[rt] = min(tree[rt << 1] , tree[rt << 1 | 1]);
}
void PushDown(int rt,int m) {
	if (add[rt]) {
		checkMax(add[rt << 1] , add[rt]);
		checkMax(add[rt << 1 | 1] ,  add[rt]);
		checkMax(tree[rt << 1] , add[rt]);
		checkMax(tree[rt << 1 | 1] , add[rt]);
		add[rt] = 0;
	}
}
void build(int l,int r,int rt) {
	add[rt] = 0;tree[rt] = 0;
	if (l + 1== r) {

		return ;
	}
	int m = (l + r) >> 1;
	build(lson);
	build(rson);
	PushUp(rt);
}
void update(int L,int R,int c,int l,int r,int rt) {
	if (L <= l && r <= R) {
		checkMax(add[rt] , c);
		checkMax(tree[rt] , c);
		return ;
	}
	if (l + 1 >= r) return;
	PushDown(rt , r - l + 1);
	int m = (l + r) >> 1;
	if (L <= m) update(L , R , c , lson);
	if (m < R) update(L , R , c , rson);
	PushUp(rt);
}
int query(int L,int R,int l,int r,int rt) {
	if (L <= l && r <= R) {
		return tree[rt];
	}
	if (l + 1 >= r) return 1e9 + 7;
	PushDown(rt , r - l + 1);
	int m = (l + r) >> 1;
	int ret = 1e9 + 7;
	if (L <= m) checkMin(ret , query(L , R , lson));
	if (m < R) checkMin( ret , query(L , R , rson));
	return ret;
}

VI DOT , DET;
VI all;
struct Tower{   // 塔类
    int l , r , d ;
    int v;
    void input(int x , int y){
        RD(v);
        DOT.clear();
        DET.clear();
        FOR_1(xx , x , x + 1)
            FOR_1(yy , y , y + 1){
                DET.PB( Point(xx , yy) ^ V);
                DOT.PB( Point(xx , yy) * V);
            }
        // 枚举四个点的点积和叉积
        l = *min_element( ALL(DET) )  * 2 ;
        r = *max_element( ALL(DET) )  * 2;
        all.PB(l);
        all.PB(r);
        d = *min_element( ALL(DOT) );
    }
    bool operator < (const Tower & A) const{
        return d < A.d;
    }
}newOne;
vector<Tower> Tow;
map<int , int> Po ;
void solve(){
    all.clear();
    Tow.clear();
    REP(i , n)
        REP(j , n){
            newOne.input(i , j);
            Tow.PB( newOne );
        }
    sort( ALL(Tow) );
    sort( ALL(all) );   // 离散化线段坐标
    unique( ALL(all) );
    Po.clear();
    int Tr = all.size();
    for (int i = all.size() - 1 ; i >=0 ; --i)
        Po[ all[i] ] = i + 1;
    ECH(iter , Tow){
        iter -> l = Po[ iter-> l ];
        iter -> r = Po[ iter-> r ];
    }

    build(1 , Tr , 1);
    LL ans = 0;
    ECH(iter , Tow){
        LL res = query(iter->l , iter->r , 1 , Tr , 1); // 区间最值
        if (res < (iter -> v) ){    // 更新与记录
            ans += ((iter -> v) - res);
            update(iter -> l , iter -> r  , iter -> v , 1 , Tr , 1);
        }
    }
    cout << ans << endl;
}
int main(){
    while(cin >> n >> V.x >> V.y) solve();
}









P.S.

http://codeforces.com/contest/243/submission/2588704 及其仰慕用DP破的nankai神牛- - 至于咋做就得问daodao和watashi了Orz

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值