首先,我得吐個槽。陶叔果然是個誠實的孩子,今天的題簡直坑爆了好麼?
先說A題,翻了一遍題目,覺得A題(SPOJ SUB_PROB)就是個KMP的模板題,心中大喜,套模板,欲A之,怎奈何居然是WA(我還提交了三遍T_T,再不濟你給個TLE我也可以接受啊)。於是重新讀題,發現應該拿AC自動機來搞!!!瞄一眼Rank,分析一下局勢,決定不理A題了,開了E題(Aizu 0024)。
由於E題是簽到水題(按照陶叔的話來說,什麼是簽到題捏?簽到題就是你A了以後能證明你來了的題~),果斷AC。接着順手開了F題(CodeForces 81A),發現F題也很水,是道堆棧的模擬題,以前有做過類似的題目,輕鬆A掉。看看錶才過了不到一個小時,暗自感嘆今天運氣還是不錯滴,喝口水,養個神,開D題(UVA 10319)。
由於英語太渣,讀題貌似出現了錯誤,天真的以爲是DFS,開心的碼完代碼,提交,WA。正在鬱悶的時候,看到了陶叔良心發現在公告裏給了Hint,尼瑪,2-SAT!思路差了八十條街,而且最坑爹的是,我讀錯題的DFS樣例居然過了!於是心情默默的不好了,關了OJ,開始各種騷擾劉文蔚學長。。。。。。
比完賽之後仔細看了看,其實C題(SPOJ RATING)挺好搞的,就是題目寫得不是特別順眼,一開始要是選這道的話,還是有可能A掉的~
好吧,吐完槽了,開始寫正經東西,線段樹的離散化和掃描線。
POJ上有一道題很經典,算是線段樹的進階吧,大家一起來看一下。
Description
Input
The input file is terminated by a line containing a single 0. Don't process it.
Output
Output a blank line after each test case.
Sample Input
2 10 10 20 20 15 15 25 25.5 0
Sample Output
Test case #1 Total explored area: 180.00
Source
題目意思很簡單,就是讓你求矩形的面積,重合的部分只算一次。解題的步驟大體來說分爲三步:
1)輸入數據,建樹。
2)離散化:將所有的x軸座標存在一個數組裏。
3)掃描線:從下到上掃描,更新區間計數。
圖片來自:紅黑聯盟-kk303
下面是完整的代碼:
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define MAXN 405
using namespace std;
struct node
{
double l,r,y;//左端點,右端點,y軸高度
int tp;//上下水平線標記
bool operator < (node a) const// < 運算符重載,sort函數使用
{
return y < a.y;
}
}line[MAXN << 2];
int n;
double X[MAXN << 2],Times[MAXN << 2],sum[MAXN];
int b_search(double x)//區間查找
{
int l,r,mid;
l = 0,r = n + 1;
while (r - l > 1)
{
mid=(l + r) >> 1;
if (X[mid] <= x) l = mid;
else r = mid;
}
return l;//返回離散化的區間
}
void update(int x,int c,int l,int r,int now)
{
if (l == r)
{
Times[x] += c;//區間計數修改
if (Times[x]) sum[now]=X[x+1]-X[x]; //若區間計數爲正,得到區間長度
if (!Times[x]) sum[now]=0;//若區間計數爲零,不計入長度
return;
}
int mid = (l + r )/ 2;
if (x <= mid) update(x,c,l,mid,now << 1);
if (mid < x) update(x,c,mid + 1,r,(now << 1) | 1);
sum[now] = sum[now << 1] + sum[(now << 1) | 1];
return;
}
int main()
{
int i,j,T=0;
double ans=0;
while (~scanf("%d",&n) && n)
{
int num=0;
for (i=1;i<=n;i++)
{
double x1,y1,x2,y2;
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);//錄入矩形的對角線端點
line[i*2-1].y = y1;//矩形水平線的y軸座標
line[i*2-1].l = x1;//水平線的左端點
line[i*2-1].r = x2;//水平線的右端點
line[i*2-1].tp = 1;//下水平線標記
line[i*2].y = y2;//矩形水平線的y軸座標
line[i*2].l = x1;//水平線的左端點
line[i*2].r = x2;//水平線的右端點
line[i*2].tp = -1;//上水平線標記
X[++num]=x1;//矩陣的左端點
X[++num]=x2;//矩陣的右端點
}
ans = 0;
n = n * 2;//每個矩陣都有上下水平線
sort(X + 1,X + 1 + num);//將所有X座標從小到大排序
sort(line + 1,line + 1 + n);//將所有line按Y座標從小到大排序
memset(sum,0,sizeof(sum));//掃描線掃到的合法長度
memset(Times,0,sizeof(Times));//區間的計數數組
for (i = 1;i <= n;i++)
{
ans += sum[1] * (line[i].y - line[i - 1].y);//面積 = 每一段合法長度 * 高度
int l,r;
l = b_search(line[i].l);//離散化,獲取區間
r = b_search(line[i].r) - 1;//離散化,獲取區間
for (j = l;j <= r;j++)
update(j,line[i].tp,1,n-1,1);//掃描線更新操作
}
printf("Test case #%d\nTotal explored area: %.2f\n\n",++T,ans);
}
return 0;
}