2019.08.07【NOIP提高组】模拟 B 组 计算几何+线段树/树状数组+仙人掌+数论

21 篇文章 0 订阅
8 篇文章 0 订阅

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 ( n 2 l o g n ) O(n^2 log_n) O(n2logn)

#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》

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值