板子哲学
不是说这里面的都是板子(
密码:#39C5BB
图论
数学
字符串
数据结构
搜索与递推
STL标准库
set 与 multiset
multiset
和set
的区别
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
,否则返回true
,s.none()
相反。 -
s.set()
将 \(s\) 的所有位变为 \(1\),s.reset()
将 \(s\) 的所有位都变为 \(0\),s.flip()
将 \(s\) 的所有位取反。
vector
比赛时申请双倍内存,谨慎使用。
杂项
(一些有意思但是没有用的东西?)
%:
可以代替 #
<% %>
可以代替 { }
有关评测环境
//注意,以下语句最多图个方便,不能在真正的比赛上使用。
#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内部
}