1. 程式人生 > 其它 >《深入理解Java虛擬機器》(六) 調優策略 -- 筆記

《深入理解Java虛擬機器》(六) 調優策略 -- 筆記

掃描線

掃描線+離散化+線段樹

(以下引用自@kk303的部落格)

初始狀態:

掃到最下面的線,點更新為紅色部分:

掃到下一根線,求出綠色部分的面積,加入答案,更新計數器:

同上,求出黃色部分面積,加入答案:

同上,求出灰色部分面積,加入答案:

同上,求出紫色部分面積,加入答案:

同上,求出藍色部分面積,加入答案:

掃描到最後一條邊了,結束!

面積並

Luogu P5490 掃描線

就是掃描線,純的

\(code\)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define maxn 200010//線段樹4倍空間!!! 
#define int long long
using namespace std;
template<typename T>
inline void read(T &x){
	x=0;bool flag=0;char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
	for(;isdigit(c);c=getchar()) x=x*10+(c^48);
	if(flag) x=-x;
}

int n,x1,x2,y1,y2,ans;
int cnt,X[maxn];
int t[2*maxn],tag[2*maxn],rt,ls[2*maxn],rs[2*maxn];
struct data{
	int l;
	int r;
	int h;
	int k;//權值1/-1 
}lin[maxn];

bool cmp(data x,data y){
	if(x.h==y.h) return x.k>y.k;//********
	return x.h<y.h;
}

void pushup(int x,int l,int r){
	if(tag[x]) t[x]=X[r+1]-X[l];
	else t[x]=t[ls[x]]+t[rs[x]];
}

void update(int &x,int l,int r,int cl,int cr,int k){
	if(!x) x=++cnt;
	if(cl<=l&&cr>=r){
		tag[x]+=k;
		pushup(x,l,r);
		return ;
	}
	int mid=(l+r)/2;
	if(cl<=mid) update(ls[x],l,mid,cl,cr,k);
	if(cr>=mid+1) update(rs[x],mid+1,r,cl,cr,k);
	pushup(x,l,r);
}

signed main(){
	read(n);
	for(int i=1;i<=n;i++){
		read(x1),read(y1),read(x2),read(y2);
		X[i*2]=x1,X[i*2-1]=x2;
		lin[i*2]=(data){x1,x2,y1,1};
		lin[i*2-1]=(data){x1,x2,y2,-1};
	}
	sort(X+1,X+2*n+1);
	sort(lin+1,lin+2*n+1,cmp);
	int m=unique(X+1,X+2*n+1)-X-2;//去重 
	for(int i=1;i<=2*n;i++){
		int l=lower_bound(X+1,X+m+1,lin[i].l)-X;
		int r=lower_bound(X+1,X+m+1,lin[i].r)-X-1;
		//--------------
//		cout<<l<<" "<<r<<endl;
//		cout<<rt<<endl;
//		cout<<t[rt]<<endl;
//		cout<<lin[i].h-lin[i-1].h<<endl;
//		cout<<"*****"<<endl;
		ans+=t[rt]*(lin[i].h-lin[i-1].h);
		update(rt,1,m,l,r,lin[i].k);
	} 
	cout<<ans<<endl;
	return 0;
}
/*
2
100 100 200 200
150 150 250 255
//
18000
*/

HDU 1542 Atlantis

也是板子,就不寫了

面積交

HDU 1255

題目求的情況很好處理,覆蓋至少兩次的都可以,所以我們用 \(tag\) 來記錄該節點表示的區間完全被覆蓋。

\(tag \ge 2\) 表示該區間被完全覆蓋 \(\ge 2\) 次,這正是我們需要的。 \(tag=1\) 表示被完全覆蓋切覆蓋長度就是區間長度。 \(tag=0\) 表示沒被完全覆蓋,但是可能覆蓋一部分,所以要算出該區間中被覆蓋的長度。

由此我們可以算出被覆蓋一次和被覆蓋兩次的長度,我們用 \(one\)\(two\) 來存。

為什麼這裡存的只是長度呢?因為由掃描線的原理可得,我們是隨著水平線的掃描來一塊一塊加入答案的,高度直接由相減可得,這裡就只記錄要加入的矩形的長度即可。

推廣一下,若求覆蓋3次,4次...也可以這樣搞,當然次數太多就不行了。

  • 其實樣例大概是有誤差的,\(7.63->7.62\) 在這上面調一年

  • 對於題目中的 注意:本題的輸入資料較多,推薦使用 \(scanf\) 讀入資料. ,其實 \(cin\) 也是可過的(親測

  • 空間要開夠!別問我怎麼知道的

\(code\)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define maxn 4010//線段樹4倍空間!!! 
#define int long long
using namespace std;
template<typename T>
inline void read(T &x){
	x=0;bool flag=0;char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
	for(;isdigit(c);c=getchar()) x=x*10+(c^48);
	if(flag) x=-x;
}

int t,n;
int tag[2*maxn];
double x1,x2,y1,y2,ans;
double X[maxn],one[2*maxn],two[2*maxn],ls[2*maxn],rs[2*maxn];
struct data{
	double l;
	double r;
	double h;
	int k;
}lin[2*maxn];

bool cmp(data x,data y){
	if(x.h==y.h) return x.k>y.k;
	return x.h<y.h;
}

void pre(){
	ans=0;
	memset(tag,0,sizeof(tag));
	memset(one,0,sizeof(one));
	memset(two,0,sizeof(two));
}

void pushup(int x,int l,int r){
	if(tag[x]>=2) two[x]=one[x]=X[r+1]-X[l];
	if(tag[x]==1){
		one[x]=X[r+1]-X[l];
		if(l==r) two[x]=0;
		else two[x]=one[x*2]+one[x*2+1];
	}
	if(tag[x]==0){
		if(l==r) one[x]=two[x]=0;
		else{
			one[x]=one[x*2]+one[x*2+1];
			two[x]=two[x*2]+two[x*2+1];
		}
	}
}

void update(int x,int l,int r,int cl,int cr,int k){
	if(cl<=l&&cr>=r){
		tag[x]+=k;
		pushup(x,l,r);
		return ;
	}
	int mid=(l+r)/2;
	if(cl<=mid) update(x*2,l,mid,cl,cr,k);
	if(cr>=mid+1) update(x*2+1,mid+1,r,cl,cr,k);
	pushup(x,l,r);
}

signed main(){
	read(t);
	while(t--){
		read(n);
		for(int i=1;i<=n;i++){
			cin>>x1>>y1>>x2>>y2;
			X[i]=x1,X[i+n]=x2;
			lin[i]=(data){x1,x2,y1,1};
			lin[i+n]=(data){x1,x2,y2,-1};
		}
		sort(X+1,X+2*n+1);
		sort(lin+1,lin+2*n+1,cmp);
		pre();
		int m=unique(X+1,X+2*n+1)-X-1;
		for(int i=1;i<=2*n-1;i++){
			int l=lower_bound(X+1,X+m+1,lin[i].l)-X;
			int r=lower_bound(X+1,X+m+1,lin[i].r)-X;
			//--------------
//			cout<<l<<" "<<r<<endl;
//			cout<<rt<<endl;
//			cout<<two[rt]<<endl;
//			cout<<lin[i].h-lin[i-1].h<<endl;
//			cout<<"*****"<<endl;
			if(l<r) update(1,1,m,l,r-1,lin[i].k);
			ans+=two[1]*(lin[i+1].h-lin[i].h);
		} 
		printf("%.2lf\n",ans);
	}
	return 0; 
}
/*
2
5
1 1 4 2
1 3 3 7
2 1.5 5 4.5
3.5 1.25 7.5 4
6 3 10 7
3
0 0 1 1
1 0 2 1
2 0 3 1
//
7.63
0.00
*/
/*
1
5
1 3 5 7
2 3 15 20
2.13 5.64 3 8
1 5 4 6
6 3 10 7
//
29.87
*/

周長並

HDU 1828 Picture

在寫了,寫完更