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