板子哲学

不是说这里面的都是板子(

点击查看目录

密码:#39C5BB

图论

最短路/最小生成树

tarjan

dfs序求lca,RMQ

优化建图相关

二分图与网络流基础

数学

高斯消元

二项式定理与二项式反演

组合数学(与CRT)

概率期望

斯特林数

计算几何

字符串

Hash,KMP,Trie树

Manacher(马拉车)求回文子串

数据结构

树状数组

线段树与扫描线

分块与莫队

Hash表,Trie树

搜索与递推

记忆化搜索

meet int the middle 折半搜索

概率期望dp

数位dp

STL标准库

set 与 multiset

multisetset的区别

1.set 内部元素不能重复,而 multiset 允许元素重复。

2.multiset 支持插入、删除和查找操作的平均时间复杂度均为 O(log n),而 set 只支持插入和查找操作的平均时间复杂度为 O(log n),删除操作的平均时间复杂度为 O(1)。

3.使用 multiset 时可以保存重复元素,但需要注意如果要删除某个元素,只能删除 multiset 中所有相同元素中任意一个,而不能只删其中的一些。

相同处:

都是有序排序且都是升序

通常情况下,如果不需要保存重复元素,优先使用 set,因为 set 的查找操作更快;如果需要保存重复元素,或者需要使用 multiset 特有的功能(如保留相同元素),则使用 multiset

set<int>f[maxn];

\(f_i\).size()表示节点i的集合大小(包括自己),可以用来找出父节点。

swap(\(f_i\),\(f_j\))表示交换两个set容器

\(f_i\).lower_bound(key_value) ,返回\(f_i\)中第一个大于等于key_value的迭代器

\(f_i\).erase(iterator),删除定位器iterator指向的值

\(f_i\).begin()返回\(f_i\)中第一个元素的迭代器

\(f_i\).end()返回最后一个元素的迭代器

具体使用
//定义 
set<int> s;
set<QWQ> s;
set<vector<int> >s;     
//vector中提供重载 <
//结构体可正常重载运算符 

//插入 O(log n)
s.insert(i);

//判断是否为空 O(1) 
s.empty(); 
 
//大小 O(1) 
s.size();

//清空 
s.clear();

//值为x的元素个数 O(log n+ans)
s.count(x); 
 
//迭代器(仅支持 ++ 与 -- )
// ++ 与 -- O(logn) 
//begin 与 end O(1) 
set<int>::iterator it=s.begin();
s.begin();//队首迭代器 
s.end();//队尾迭代器,实际上--s.end才是指向集合中最大元素的迭代器(左闭右开) 
 
//查询
//在 set 中查找值为 x 的元素,并返回指向该元素的迭代器,若不存在,返回 set.end()  O(log n)
set.find(a);

//删除
//参数可以是元素或者迭代器,返回下一个元素的迭代器 O(log n)
set<int>::iterator it=s.begin();
s.erase(it);
s.erase(3);

//查找>=x的元素中最小的一个,并返回指向该元素的迭代器
s.lower_bound(x)
//查找>x的元素中最小的一个,并返回指向该元素的迭代器
s.uppers.upper_bound(x)
//若 x>最大元素 返回 s.end() 
 
//直接遍历就行
for(int i:s)
    cout<<i; 

有关set的迭代器
/*`set<int>::iterator` 表示 `set<int>` 类型的迭代器,
用于遍历 `set` 中的元素。具体来说,
它是一个指向 `set<int>` 中元素的指针,
可以通过迭代器访问 `set<int>` 中的元素,
并能够进行迭代器运算,如自增等操作。

例如,对于一个 `set<int> s`,要遍历其中的元素,
可以使用 `set<int>::iterator it` 定义迭代器,
然后使用 `begin()` 和 `end()` 函数获取 `set` 中第一个元素和尾后元素的迭代器,
再通过循环遍历 `set` 中的所有元素,示例代码如下:*/

set<int>::iterator it;
for (it = s.begin(); it != s.end(); ++it) {
    cout << *it << " ";
}

/*上述代码中,`it` 是 `set<int>` 的迭代器,
`s.begin()` 和 `s.end()` 分别返回 `set<int>` 的第一个元素和尾后元素的迭代器,
`*it` 则是获取当前迭代器指向的元素的值。*/

针对题目中代码
for(int j:f[to]){
			f[now].insert(j);
		}
//----------------------------------//
for(set<int>::iterator j=f[to].begin();j!=f[to].end();++j){
			f[now].insert(*j);
		}
是等价的。
map

map 是一个关联式容器,其提供一对一的数据处理能力,类似于 hash表,对于一个 first 元素仅指向一个 second 元素,某些题目可以让 first 作为数组下标来处理可能数组越界的需要桶的问题。

存在以下操作:

=迭代器=========
begin   返回指向容器起始位置的迭代器(iterator)
end    返回指向容器末尾位置的迭代器
cbegin  返回指向容器起始位置的常迭代器(const_iterator)
cend    返回指向容器末尾位置的常迭代器
=Capacity
size    返回有效元素个数
max_size 返回 unordered_map 支持的最大元素个数
empty 判断是否为空
=元素访问=
operator[]    访问元素
at        访问元素
=元素修改=
insert   插入元素
erase   删除元素
swap    交换内容
clear   清空内容
emplace  构造及插入一个元素
emplace_hint 按提示构造及插入一个元素
操作=========
find       通过给定主键查找元素,没找到:返回unordered_map::end
count      返回匹配给定主键的元素的个数
equal_range   返回值匹配给定搜索值的元素组成的范围
Buckets======
bucket_count    返回槽(Bucket)数
max_bucket_count 返回最大槽数
bucket_size     返回槽大小
bucket       返回元素所在槽的序号
load_factor     返回载入因子,即一个元素槽(Bucket)的最大元素数
max_load_factor   返回或设置最大载入因子
rehash       设置槽数
reserve       请求改变容器容量

map 基于红黑树实现,时间复杂度 \(O(logn)\)unordered_map 则理论达到时间复杂度 \(O(1)\),但是带有常数。

一般来说,在我的测试下,应对阴间的极限数据时,unorder_map 表现更佳。

而对于 unordered_map 还可以进行我也不知道是什么东西的加速:

#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
struct custom_hash{
    static uint64_t splitmix64(uint64_t x){
        x+=0x9e3779b97f4a7c15;
        x=(x^(x >> 30))*0xbf58476d1ce4e5b9;
        x=(x^(x >> 27))*0x94d049bb133111eb;
        return x^(x >> 31);
    }
    size_t operator()(uint64_t x) const{
        static const uint64_t FIXED_RANDOM=std::chrono::steady_clock::now().time_since_epoch().count();
        return splitmix64(x + FIXED_RANDOM);
    }
};
__gnu_pbds::gp_hash_table<int,int,custom_hash> f;

在比较水的数据上并没有很大的体现,在 cf 的 1598F - RBS 题目上进行测试,共 124 个测试点,时间限制 3000ms,以下是对比:

  • map 卡在第 93 个测试点上,该测试点为极限数据,超时,内存 111500 KB。

  • unordered_map 用时 1169 ms,内存 112900 KB。

  • gp_hash_table 用时 779 ms,内存 110900 KB。

pair

可以把 pair 直接看做一个以第一元素为关键字升序排序的结构体。

(跑堆优化 dijistra 非常好用)

make_pair 就是把两个数构造一个 pair

bitset

std::bitset 可以看做一个二进制数,每 \(8\) 位占用一个字节,支持基本的位运算,可用于状态压缩,\(n\)bitset 执行单次运算的时间复杂度为 \(O\left(\dfrac{n}{32}\right)\)

bitset 的主要优势在位运算极快,据说在 \(n\leq 10^5\) 的情况下,\(O(n^2)\) 的算法 bitset 可过。

因为定义的时候就已经确定了位数所以操作时要求位数相同。

操作:

  • 定义:std::bitset<n> s(x) 表示定义一个 \(n\) 位的二进制数,数值上等于 \(x\)

  • s[k] 表示 \(s\) 的第 \(k\) 位(从右往左,编号从 \(0\) 开始),可以赋值或取值。

  • ~s 对二进制数 \(s\) 取反。

  • &|^ 返回两个位数相同的 bitset 按位与、或、亦或运算结果。

  • <<>>返回一个 bitset 左移、右移若干位的结果(补 \(0\))。

  • ==!= 比较两个位数相同的 bitset 是否相等。

  • s.any() 当所有位都为 \(0\) 时返回 false,否则返回 trues.none() 相反。

  • s.set()\(s\) 的所有位变为 \(1\)s.reset()\(s\) 的所有位都变为 \(0\)s.flip()\(s\) 的所有位取反。

vector 比赛时申请双倍内存,谨慎使用。

杂项

(一些有意思但是没有用的东西?)

%: 可以代替 #

<% %> 可以代替 { }

慎用 inline

有关评测环境
//注意,以下语句最多图个方便,不能在真正的比赛上使用。


#ifndef ONLINE_JUDGE
freopen("T1.in","r",stdin);
#endif
//在评测环境下不执行如上freopen语句 
//——————————————————————————————————————————————————————————//
#if ONLINE_JUDGE 
char in[1<<20],*p1=in,*p2=in;
#define getchar() (p1==p2&&(p2=(p1=in)+fread(in,1,1<<20,stdin),p1==p2)?EOF:*p1++)
#endif
//若是评测环境则开启fread 
//——————————————————————————————————————————————————————————//
#if ONLINE_JUDGE
#define gh() (iS == iT ? iT = (iS = ibuf) + fread(ibuf, 1, (1 << 20) + 1, stdin), (iS == iT ? EOF : *iS++) : *iS++)
#else
#define gh() getchar()
#endif
//在评测环境和其他环境的双定义
快读模板
//char in[1<<20],*p1=in,*p2=in;
//#define getchar() (p1==p2&&(p2=(p1=in)+fread(in,1,1<<20,stdin),p1==p2)?EOF:*p1++)
//加上上两行是fread,1<<20是你读入的最大的可能的数的位数
//用fread调试时输出结果需要按crtl+z
inline int read(){

    char c=getchar();
    int x=0,f=1;
    while(c<48)<%if(c=='-')f=-1;c=getchar();%>
    while(c>47)x=(x*10)+(c^48),c=getchar();
    return x*f;
}
快写模板
inline void write(int x){
    if(x<0)<%putchar('-');x=~x+1;%>
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
fread,fwrite
constexpr auto SIZE(1<<20);
char in[SIZE],out[SIZE],*p1=in,*p2=in,*p3=out;
#define getchar() (p1==p2&&(p2=(p1=in)+fread(in,1,SIZE,stdin),p1==p2)?EOF:*p1++)
#define flush() (fwrite(out,1,p3-out,stdout))
#define putchar(x) (p3==out+SIZE&&(flush(),p3=out),*p3++=(x))
class Flush{public:~Flush(){flush();}}_;
inline int read(){
	int x(0);bool f(0);char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar()) f^=ch=='-';
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
	return f?x=-x:x;
}
inline void write(int x){
	x<0?x=-x,putchar('-'):0;static short Sta[50],top(0);
	do{Sta[++top]=x%10;x/=10;}while(x);
	while(top) putchar(Sta[top--]|48);
	putchar('\n');
}
对拍模版
#include<bits/stdc++.h>
#include<random>

std::mt19937 ewq(time(nullptr));
inline int get(int l,int r){					//得到l-r区间的数 
	std::uniform_int_distribution<> QAQ (l,r);	//<>不填写默认为int类型
	// uniform_int_distribution生成整数,<>不填写默认为int 
	//uniform_real_distribution生成实数 ,<>不填写默认为double 
	return QAQ(ewq);
}

int main(){
	freopen("in.txt","w",stdout);				//输入文件 
	int n,m;
	n=get(1,831),m=get(1,39); 				//8月31是miku的生日哦 ,3月9是初音未来日哦 
	printf("%d %d\n",n,m);
	for(int i=1;i<=n;++i){
		int x=get(114514,1919810);						
		printf("%d\n",x);
	} 
	return 0; 
}

/*
用正确的暴力
freopen("in.txt","r",stdin);
freopen("ans.txt","w",stdout);
得到正确答案之后

用认为的正解 
freopen("in.txt","r",stdin);
freopen("myout.txt","w",stdout);
与正确答案比对 
*/ 

//———————————————————————————而对拍也不一定要手动对拍,可以再开一个cpp来实现自动对拍,在 linux 环境下————————————————————————————//

#include<bits/stdc++.h>
#include<random>
#include<ctime>

int main(){
	system("g++ T1++.cpp -o my -O2");
	system("g++ T1f.cpp -o bl -O2");
	system("g++ data.cpp -o mkd -O2");
	//编译你的代码
	int T=0;
	while(++T){
		system("./mkd>T1.in");
        double begin=clock();
		system("./my<T1.in>my.out");
		double end=clock();
		system("./bl<T1.in>bl.out");
		double t=(end-begin);
		// 运行
		// “<in.in”表示从in.in读入数据
		// ">my.out"从my.out输出数据
		if(system("diff my.out bl.out")){
        //判断是否正确
			puts("Wrong Answer"); break;
		}
		else printf("Accepted√ %d\ntime=%.01lfms\n",T,t);
		if(t>1000){
			printf("Time Limit Exceeded %.01lfms\n",t);
			break;
		}
	}
	return 0;
}

火车头
//洛谷使用火车头会编译错误


%:pragma GCC optimize(3)
%:pragma GCC optimize("Ofast")
%:pragma GCC optimize("inline")
%:pragma GCC optimize("-fgcse")
%:pragma GCC optimize("-fgcse-lm")
%:pragma GCC optimize("-fipa-sra")
%:pragma GCC optimize("-ftree-pre")
%:pragma GCC optimize("-ftree-vrp")
%:pragma GCC optimize("-fpeephole2")
%:pragma GCC optimize("-ffast-math")
%:pragma GCC optimize("-fsched-spec")
%:pragma GCC optimize("unroll-loops")
%:pragma GCC optimize("-falign-jumps")
%:pragma GCC optimize("-falign-loops")
%:pragma GCC optimize("-falign-labels")
%:pragma GCC optimize("-fdevirtualize")
%:pragma GCC optimize("-fcaller-saves")
%:pragma GCC optimize("-fcrossjumping")
%:pragma GCC optimize("-fthread-jumps")
%:pragma GCC optimize("-funroll-loops")
%:pragma GCC optimize("-fwhole-program")
%:pragma GCC optimize("-freorder-blocks")
%:pragma GCC optimize("-fschedule-insns")
%:pragma GCC optimize("inline-functions")
%:pragma GCC optimize("-ftree-tail-merge")
%:pragma GCC optimize("-fschedule-insns2")
%:pragma GCC optimize("-fstrict-aliasing")
%:pragma GCC optimize("-fstrict-overflow")
%:pragma GCC optimize("-falign-functions")
%:pragma GCC optimize("-fcse-skip-blocks")
%:pragma GCC optimize("-fcse-follow-jumps")
%:pragma GCC optimize("-fsched-interblock")
%:pragma GCC optimize("-fpartial-inlining")
%:pragma GCC optimize("no-stack-protector")
%:pragma GCC optimize("-freorder-functions")
%:pragma GCC optimize("-findirect-inlining")
%:pragma GCC optimize("-fhoist-adjacent-loads")
%:pragma GCC optimize("-frerun-cse-after-loop")
%:pragma GCC optimize("inline-small-functions")
%:pragma GCC optimize("-finline-small-functions")
%:pragma GCC optimize("-ftree-switch-conversion")
%:pragma GCC optimize("-foptimize-sibling-calls")
%:pragma GCC optimize("-fexpensive-optimizations")
%:pragma GCC optimize("-funsafe-loop-optimizations")
%:pragma GCC optimize("inline-functions-called-once")
%:pragma GCC optimize("-fdelete-null-pointer-checks")
//在使用了快读的前提下,加在头文件前面,应对较强数据,可以优化 100ms+
//东西挺多的,有优化快读的,有手动开O3的
各类排序
  • 插入排序 \(O(n)-O(n^2)\):把 \(n\) 个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有 \(n-1\) 个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。

  • 冒泡排序 \(O(n)-O(n^2)\):单次将最大的元素往前冒泡。

  • 归并排序 \(O(n)-O(nlogn)\):分治,将两个的有序数列合并成一个有序数列。

  • 快速排序 \(O(n)-O(n^2)\):分治,是冒泡排序的改进,通常时间复杂度低于 \(O(nlogn)\)

  • 桶排序 \(O(n)-O(n^2)\):设置一个定量的数组当作空桶,遍历序列,并将元素一个个放到对应的桶中,对每个不是空的桶进行排序,从不是空的桶里把元素再放回原来的序列中。平均是 \(O(n+\frac{n^2}{k}+k)\) 的(将值域平均分成 n 块 + 排序 + 重新合并元素)。

  • 计数排序 \(O(n+w)\)\(w\) 指值域最大值):计算每个数出现了几次,求出每个数出现次数的前缀和,利用出现次数的前缀和,从右至左计算每个数的排名。

  • 基数排序 \(O\left(nk\right)\)\(k\) 指进制下最大位数),详见下阅读程序 17.

  • 希尔排序 \(O(n)-O(n^2)\):这是一个不稳定的排序,时间复杂度通常为 \(O(nlog^2n)\)

  • 堆排序 \(O(nlogn)\):这是一个不稳定的排序,首先建立大顶堆,然后将堆顶的元素取出,作为最大值,与数组尾部的元素交换,并维持残余堆的性质,之后将堆顶的元素取出,作为次大值,与数组倒数第二位元素交换,并维持残余堆的性质,以此类推,在第 \(n-1\) 次操作后,整个数组就完成了排序。

查理线段树

诞生

如题,将 id<<1 改为 mid<<1,将 id<<1|1 改为 mid<<1|1 即可,可以将线段树的空间复杂度可以从 \(4n\) 降为 \(2n\)

证明:

评测对比:题目:[JOISC2022] 监狱 (本题需要线段树优化建图因此建了两棵线段树,且测试点共 \(110\) 个全部通过足以证明查理线段树的正确性)

正常线段树

查理线段树

于是你发现它没什么用(起码在动态内存评测的情况下,是这样的)

看个乐呵得了。

assert 函数

assert(表达式) 是 assert 函数的基本用法,需调用 <assert.h> 库。

其主要的作用就是如果括号内表达式成立,停止程序运行。

Sonnety自用封装九九新
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define rg register int
typedef long double llf;
typedef long long ll;
typedef pair<int,int> PII;
const double eps=1e-8;
namespace io{
	#if ONLINE_JUDGE 
	char in[1<<20],*p1=in,*p2=in;
	#define getchar() (p1==p2&&(p2=(p1=in)+fread(in,1,1<<20,stdin),p1==p2)?EOF:*p1++)
	#endif
	//若是评测环境则开启fread 
	il int read(){
   		char c=getchar();
    	int x=0,f=1;
   		while(c<48)<%if(c=='-')f=-1;c=getchar();%>
    	while(c>47)x=(x*10)+(c^48),c=getchar();
    	return x*f;
	}
	//快读
	il void write(int x){
    	if(x<0)<%putchar('-');x=~x+1;%>
    	if(x>9) write(x/10);
   		putchar(x%10+'0');
	} 
	//快写
	il int ins(char *str){
	 	int len=0;
	  	while(1){
			char c=getchar();
			if(c!='\n' && c!='\0' && c!='\r')	str[++len]=c;
			else{
				//if(len==0)	continue; 有些数据可能因为造的数据有问题而最后出现重复空格或换行
		    	break;
			}
	  	}
		return len;
	}
	//getchar快速读入字符串并返回字符串长度,下标从1开始 
}
namespace mystd{
	il int Max(int a,int b)<%if(a<b) return b;return a; %>
	il int Min(int a,int b)<%if(a>b) return b;return a; %>
	il int Abs(int a)<% if(a<0) return a*(-1);return a; %>
	//整数 
	il double fMax(double a,double b)<%if(a<b) return b;return a; %>
	il double fMin(double a,double b)<%if(a>b) return b;return a; %>
	il double fAbs(double a)<% if(a<0) return a*(-1);return a; %>
	//浮点数 
	il int dcmp(double a){
		if(a<-eps)	return -1;
		if(a>eps)	return 1;
		return 0;
	}
	//精度处理 
}

namespace mathstd{
	const int mod=1e9+7;
	int gcd(int x,int y){
		if(y==0)	return x;
		else	return gcd(y,x%y);
	}
	int lcm(int x,int y)<% return (x/gcd(x,y))*y; %>
	//辗转相除最大公约数or最小公倍数
	il ll qpow(ll x,int y){
		ll res=1;
		while(y){
			if(y&1)	res=res*x%mod;
			x=x*x%mod;
			y=y>>1;
		}
		return res;
	}
	//快速幂求x^y
	il ll getinv(ll x)<%	return qpow(x,mod-2); %>
	//费马小定理求逆元 
	int x,y; 
	void exgcd(int a,int b){
		if(b==0)<% x=1;y=0; %>
		else{
			exgcd(b,a%b);
			int save=x;
			x=y,y=save-(a/b)*y;
		}
	}
	//拓展欧几里得求a在模b意义下的逆元x 
	const int maxn=1e7+50;
	int fac[maxn],inv[maxn],facinv[maxn];
	il void pre_facinv(int w){
		fac[0]=fac[1]=1; inv[0]=inv[1]=1; facinv[0]=facinv[1]=1;
		for(rg i=2;i<=w;++i)<% fac[i]=i*fac[i-1]%mod; %>
		for(rg i=2;i<=w;++i)<% inv[i]=(mod-mod/i*inv[mod%i]%mod+mod)%mod; %>
		for(rg i=2;i<=w;++i)<% facinv[i]=facinv[i-1]*inv[i]%mod; %>
	}
	//线性求阶乘逆元
	il ll getc(ll y,ll x){
		if(x<y)	return 0;
		return fac[x]%mod*facinv[y]%mod*facinv[x-y]%mod;
	}
	ll lucas(ll y,ll x){
		if(y==0)	return 1;
		return getc(y%mod,x%mod)%mod*lucas(y/mod,x/mod)%mod;
	}
	//lucas定理求组合数C(x,y)
	int n;
	int CRT(int m[],int r[]){
		int mod=1,ans=0;
		for(int i=1;i<=n;++i){
			mod*=m[i];
		}
		for(int i=1;i<=n;++i){
			int c=mod/m[i],x=0,y=0;
			exgcd(c,m[i]);
			ans=(ans+r[i]*c*x%mod)%mod;
		}
		return (ans%mod+mod)%mod;
	}
	//中国剩余定理求解互质的互余方程组 
}

struct Node<% double x,y; Node(double xx=0,double yy=0)<% x=xx,y=yy; %> %>;
il bool operator==(Node a,Node b)<% return !mystd::dcmp(a.x-b.x) && !mystd::dcmp(a.y-b.y); %>
il Node operator+(Node a,Node b)<% return Node(a.x+b.x,a.y+b.y); %>
il Node operator-(Node a,Node b)<% return Node(a.x-b.x,a.y-b.y); %> 
il Node operator*(Node a,double k)<% return Node(a.x*k,a.y*k); %>
namespace Vector{
	il double dot(Node a,Node b)<% return a.x*b.x+a.y*b.y; %>				//点乘 	
	il double len(Node a)<% return sqrt(dot(a,a)); %>						//模长
	il double angle(Node a,Node b)<% return acos(dot(a,b)/len(a)/len(b)); %>//夹角 
	il double cro(Node a,Node b)<% return a.x*b.y-a.y*b.x; %>				//叉积 
	il int judge_LINE(Node p,Node a,Node b)<% return !mystd::dcmp(cro(p-a,b-a)); %>
	//判断p是否在直线AB上
	il int judge_line(Node p,Node a,Node b)<% return !mystd::dcmp(cro(p-a,b-a)) && mystd::dcmp(mystd::fMin(a.x,b.x)-p.x)<=0 && mystd::dcmp(p.y-mystd::fMax(a.y,b.y))<=0 && mystd::dcmp(p.x-mystd::fMax(a.x,b.x))<=0 && mystd::dcmp(mystd::fMin(a.y,b.y)-p.y)<=0; %>
	//判断p是否在线段AB上 
	il Node footnode(Node p,Node a,Node b){
		Node x=p-a,y=p-b,z=b-a;
		double len1=dot(x,z)/len(z),len2=-1.0*dot(y,z)/len(z);
		return a+z*(len1/(len1+len2));
	}
	//求p到直线AB的垂足
	il Node symmetry(Node p,Node a,Node b)<% return p+(footnode(p,a,b)-p)*2; %>
	//求p到直线AB的对称点
	il Node internode(Node a,Node b,Node c,Node d)<% Node x=b-a,y=d-c,z=a-c;return a+x*(cro(y,z)/cro(x,y)); %>
	//直线AB与CD交点 
	il int judge_CROSS(Node a,Node b,Node c,Node d)<% return judge_line(internode(a,b,c,d),c,d); %>
	//判断直线AB与线段CD是否有交点 
	il int judge_cross(Node a,Node b,Node c,Node d){
		double c1=cro(b-a,c-a),c2=cro(b-a,d-a);
		double d1=cro(d-c,a-c),d2=cro(d-c,b-c);
		return mystd::dcmp(c1)*mystd::dcmp(c2)<0 && mystd::dcmp(d1)*mystd::dcmp(d2)<0;
	}
	//判断线段AB与线段CD是否有交点 
	il int PIP(Node *P,int n,Node a){
		int cnt=0,j;
		double res=0.0;
		for(rg i=1;i<=n;++i){
			if(i<n)	j=i+1;
			else	j=1;
			if(judge_line(a,P[i],P[j]))	return 2;//点在多边形上
			if(a.y>=mystd::fMin(P[i].y,P[j].y) && a.y<mystd::fMax(P[i].y,P[j].y)){
			//纵坐标在该线段两端点之间 
				res=P[i].x+(a.y-P[i].y)/(P[j].y-P[i].y)*(P[j].x-P[i].x);
				//res是同一纵坐标下的在该边上的点的横坐标 
				cnt=cnt+(mystd::dcmp(res-a.x)>0);
			} 
		}
		return cnt&1;		//奇数次就是在多边形内 
	}
	//判断点a是否在多边形P内部 
}
posted @ 2023-06-23 11:29  Sonnety  阅读(224)  评论(0编辑  收藏  举报