2019.08.07【NOIP提高組】模擬 B 組 計算幾何+線段樹/樹狀數組+仙人掌+數論

0 直角三角形

二維平面座標系中有N個點。

從N個點選擇3個點,問有多少選法使得這3個點形成直角三角形。

直角三角形的判斷:
1.勾股
2.叉積
3.斜率

1.暴力n^3,在線TLE
七夕你要怎麼過,n^3卡常吸氧過
但是NOIP不能吸氧哦

#pragma GCC optimize(3)            //O3優化
#pragma GCC optimize(2)           //O2優化
#include <cstdio> 
#include <iostream>

using namespace std;

int n,ans;
long long d[1503][1503];
long long x[1503],y[1503];

int init(){
	char ch=getchar();
	int c=0,bl=1;
	while (ch<'0'||ch>'9') {if (ch=='-') bl=-1;ch=getchar();}
	while (ch>='0'&&ch<='9') c=c*10+ch-'0',ch=getchar();
	return c*bl;
}

int main(){
	n=init();
	for (int i=1;i<=n;i++){
		x[i]=init();y[i]=init();
		for (int j=1;j<=i;j++)
			d[i][j]=d[j][i]=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
	}
	for (int i=1;i<=n;i++){
		for (int j=i+1;j<=n;j++)
			for (int k=j+1;k<=n;k++)
			if (d[i][j]+d[j][k]==d[i][k]||d[i][j]+d[i][k]==d[j][k]||d[i][k]+d[j][k]==d[i][j])
				ans++;
	}
	cout<<ans;
}

2.極角排序
所選擇的方法是對斜率排序
枚舉直角頂點,以選定的直角頂點作爲原點建立座標系(即每個點的座標-該點),每個點又在該座標系中與原點連線,再把所有點都旋轉至第一象限(注意座標軸上的也要移到同一軸上)。
然後對已經轉至第一象限的點們排序,排序關鍵字爲斜率。爲了精度,斜率不用除法。斜率本來是y/x,比較時就是y1/x1=y2/x2,等式運算後就是x1y2=x2y1,那麼排序時用到的比較語句就是x1y2<x2y1或x1y2>x2y1
在第一象限中,與原點連線在一條直線上且象限相鄰的點可以與原點形成直角三角形

O(n2logn)O(n^2 log_n)

#include <cstdio> 
#include <iostream>
#include <algorithm>

using namespace std;

int n,ans;
long long d[1503][1503];
long long x[1503],y[1503];
struct awm{
	long long e,f;
	int qua;
}awk[1503];

int init(){
	char ch=getchar();
	int c=0,bl=1;
	while (ch<'0'||ch>'9') {if (ch=='-') bl=-1;ch=getchar();}
	while (ch>='0'&&ch<='9') c=c*10+ch-'0',ch=getchar();
	return c*bl;
}

void turn(int i){
	swap(awk[i].e,awk[i].f);
	awk[i].f=-awk[i].f;
	awk[i].qua=(awk[i].qua+1)%4;
}

bool comp(awm a,awm b){
	return a.e*b.f<a.f*b.e;		//對斜率排序的判斷
}

int main(){
	n=init();
	for (int i=1;i<=n;i++){
		x[i]=init();y[i]=init();
	}
	for (int i=1;i<=n;i++){
		for (int j=1;j<=n;j++){			//選定直角頂點作爲原點
			awk[j].e=x[j]-x[i];
			awk[j].f=y[j]-y[i];
			awk[j].qua=0;
			if (i==j) awk[j]=awk[1];else
			while (awk[j].e<=0||awk[j].f<0) turn(j);//旋轉至第一象限
		}
		int j=2;
		sort(awk+2,awk+1+n,comp);                    //對斜率排序
		while (j<=n){
			int k=j;
			int an[5]={0,0,0,0};
			while (k<=n&&awk[k].e*awk[j].f==awk[k].f*awk[j].e)//是否在同一直線上
				an[awk[k].qua]++,k++;
			for (int l=0;l<4;l++)
				ans+=an[l]*an[(l+1)%4];//只有相鄰象限可以形成直角
			j=k;
		}
	}
	cout<<ans;
}

1 排序

你收到一項對數組進行排序的任務,數組中是1到N個一個排列。你突然想出以下一種特別的排序方法,分爲以下N個階段:

•階段1,把數字1通過每次交換相鄰兩個數移到位置1;

•階段2,用同樣的方法把N移到位置N;

•階段3,把數字2移到位置2處;

•階段4,把數字N-1移到位置N-1處;

•依此類推。

換句話說,如果當前階段爲奇數,則把最小的未操作的數移到正確位置上,如果階段爲偶數,則把最大的未操作的數移到正確位置上。

寫一個程序,給出初始的排列情況,計算每一階段交換的次數。
————————————————————————————————————————————————————————————

根據BPM大佬的解析,…(經過一段時間的努力)…顯然易得可將每一個數字標記爲1,每次移動都把數字的標記改爲0,交換次數即數字的當前位置到目標位置共有多少個1(#/ /~ \ \ #)並用線段樹進行區間運算
當然樹狀數組也可,而且碼量小,常數小

#include <cstdio>

using namespace std;

const int N=100005;
int n,a[N],b[N],cnt,q;
struct cv{
	int st,ed,s,ls,rs,fa;
}t[N*5];

void build(int l,int r,int f,int &c){
	t[++cnt].st=l,t[cnt].ed=r,t[cnt].fa=f;
	c=cnt;
	if (l!=r){
		int cc;
		build(l,(l+r)/2,c,cc);
		t[c].ls=cc;
		build((l+r)/2+1,r,c,cc);
		t[c].rs=cc;
		t[c].s=t[t[c].ls].s+t[t[c].rs].s;
	}else{
		t[cnt].s=1;
	}
}

int find(int x,int l,int r){
	if (l==t[x].st&&r==t[x].ed) return t[x].s;
	if (l<=t[t[x].ls].ed&&r>=t[t[x].rs].st){
		int cs=find(t[x].ls,l,t[t[x].ls].ed);
		cs+=find(t[x].rs,t[t[x].rs].st,r);
		return cs;
	}else
	if (r<=t[t[x].ls].ed) return find(t[x].ls,l,r);
	else return find(t[x].rs,l,r);
}

void change(int x){
	int c=1;
	while (t[c].st!=x||t[c].ed!=x){
		if (x<=t[t[c].ls].ed) c=t[c].ls;
						else  c=t[c].rs;
	}
	t[c].s=0;c=t[c].fa;
	while (c>=1){
		t[c].s--;
		c=t[c].fa;
	}
}

int main(){
	scanf("%d",&n);
	build(1,n,0,q);
	for (int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		b[a[i]]=i;
	}
	for (int i=1;i<=n/2;i++){
		int k=i;
		if (b[k]-1<1) printf("0\n");else
		printf("%d\n",find(1,1,b[k]-1));
		change(b[k]);
		k=n-i+1;
		if (b[k]+1>n) printf("0\n");else
		printf("%d\n",find(1,b[k]+1,n));
		change(b[k]);
	}
	if (n%2==1) printf("0");
}

2 自行車賽

翠亨村舉行一場自行車賽,翠亨村有N個路口(編號1到N),另有M條雙向邊連接起來。下面有幾個定義:

•路徑:由一系列邊組成,滿足後一條邊的起點爲前一條邊的終點;

•簡單路徑:每個路口最多經過一次的路徑;

•環:起點和終點在同一個路口的簡單路徑。

保證每對路口之間至少有一條路徑相連,除此之外還滿足每條邊最多隻會出現在一個環中。

你的任務是找出最長的滿足以下兩個條件的路徑:

•起點可以在任意路口,但終點必須在1號路口;

•路徑可能多次經過同一個路口,但每條邊最多隻會經過一次。

仙人掌。。。不會

3 小L的數列

在這裏插入圖片描述

數論。。。待完成。。。

小哥哥網戀嗎?微信號多少?——《AWM》

七夕專題——《你是我的AWM》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章