目录
分治
· 归并排序(+求逆序对)
void memsort(int s,int t){
int m,i,j,k;
if(s==t)return;
m=(s+t)/2;
memsort(s,m);
memsort(m+1,t);
i=s;j=m+1;k=s;
while(i<=m&&j<=t){
if(a[i]>a[j])
r[k++]=a[i++]; //ans=ans+t-j+1;(逆序对)
else r[k++]=a[j++];
}
while(i<=m)r[k++]=a[i++];
while(j<=t)r[k++]=a[j++];
for(int i=s;i<=t;i++)a[i]=r[i];
}
· 最近点对问题(模板)
作法: 按x坐标排序,不断往下二分。
合并:d=min(dl,dr);
找[mid.x-d,mid.x+d]内所有temp点并求两两之间最小距离dt
d=min(d,dt);
代码如下
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<string.h>
#include<cmath>
#include<string>
#include<map>
using namespace std;
const int INF=2<<20;
#define ll long long
#define M 201000
int n,temp[M];
struct point{
double x,y;
}s[M];
inline int cmpx(point a,point b){return a.x<b.x||(a.x==b.x&&a.y<=b.y);}
inline int cmpy(int a,int b){
return s[a].y<s[b].y||(s[a].y==s[b].y&&s[a].x<=s[b].x);
}
inline double min_(double a,double b){return a<b?a:b;}
inline double dist(int i,int j){
double x=(s[i].x-s[j].x)*(s[i].x-s[j].x);
double y=(s[i].y-s[j].y)*(s[i].y-s[j].y);
return sqrt(x+y);
}
double deal(int l,int r){
double d=INF;
if(l==r)return d;
if(l+1==r)return dist(l,r);
int mid=(l+r)>>1;
d=min_(deal(l,mid),deal(mid+1,r));
int k=0;
for(int i=l;i<=r;i++) //找两个区间中间[mid-d,mid+d]之间的所有点
if(abs(s[mid].x-s[i].x)<d)temp[++k]=i;
sort(temp+1,temp+1+k,cmpy);
double dt;
for(int i=1;i<=k;i++)//合并中间有必要合并的点的距离,更新最小值
for(int j=i+1;j<=k&&s[temp[j]].y-s[temp[i]].y<d;j++){
dt=dist(temp[i],temp[j]);
if(d>dt)d=dt;
}
return d;
}
int main(){
// freopen("testdata.in","r",stdin);
while(cin>>n,n!=0){
for(int i=1;i<=n;i++)scanf("%lf%lf",&s[i].x,&s[i].y);
sort(s+1,s+1+n,cmpx);
printf("%.2lf\n",deal(1,n)/2);
}
// fclose(stdin);
}
· cdq分治
三维偏序问题
引例:二维偏序
三维偏序模板题:陌上花开
思路:
对第一维排序,对第二维归并排序,第三维用树状数组维护
[l,mid]放树状数组里,[mid+1,r]计算前面对后面的贡献
!要预处理相同的三元组后面对前面的贡献(因为cdq分治的时候后面的三元组不会对前面的三元组产生贡献)
//洛谷P3810陌上花开
#include<bits/stdc++.h>
using namespace std;
const int N=100007,M=200007;
struct node{
int x,y,z,v; //v是小于当前的三元组个数
bool operator <(const node &o)const{
return x<o.x||(x==o.x&&y<o.y)||(x==o.x&&y==o.y&&z<o.z);
}
}a[N],t[N];
int n,m,c[M],ans[N];
int lowbit(int x){return x&(-x);}
void updata(int k,int val){
while(k<=m){
c[k]+=val;
k+=lowbit(k);
}
}
int getsum(int k){
int sum=0;
while(k){
sum+=c[k];
k-=lowbit(k);
}
return sum;
}
void cdq(int l,int r){//类似于归并排序
int mid=(l+r)>>1;
if(l==r)return;
cdq(l,mid);cdq(mid+1,r);
int i=l,j=mid+1,k=l;
while(i<=mid&&j<=r){
if(a[i].y<=a[j].y)updata(a[i].z,1),t[k++]=a[i++];
else a[j].v+=getsum(a[j].z),t[k++]=a[j++];
}
while(i<=mid)updata(a[i].z,1),t[k++]=a[i++];
while(j<=r)a[j].v+=getsum(a[j].z),t[k++]=a[j++];
for(int i=l;i<=mid;i++)updata(a[i].z,-1);//清空树状数组
for(int i=l;i<=r;i++)a[i]=t[i];
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
sort(a+1,a+1+n);
//三元组可能有重复,所以预先算好相等的三元组后面对前面的贡献
int cnt=1;
node temp=a[n];
for(int i=n-1;i;i--){
if(temp.x==a[i].x&&temp.y==a[i].y&&temp.z==a[i].z)
a[i].v+=cnt++;
else{
temp=a[i];
cnt=1;
}
}
cdq(1,n);
for(int i=1;i<=n;i++)ans[a[i].v]++;
for(int i=0;i<n;i++)printf("%d\n",ans[i]);
}
动态逆序对
ans[i]表示在i时刻恰好删除的有多少对逆序对
time[i]<time[j] val[i]>val[j] pos[i]<pos[j] (i,j)就会对time[j]产生贡献
time[i]>time[j] val[i]>val[j] pos[i]<pos[j] (i,j)就会对time[i]产生贡献
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100007;
struct node{
int val,time,pos; //time是删除的时间
}q[N],t[N];
ll ans[N],s;
int a[N],c1[N],c2[N],n,m,pos[N],del[N],t1[N],t2[N];
//c1,c2是两个树状数组,t1,t2是清空树状数组用的
int lowbit(int x){return x&(-x);}
int updata(int k,int val,int c[]){for(;k<=n;k+=lowbit(k))c[k]+=val;}
int getsum(int k,int c[]){int sum=0;for(;k;k-=lowbit(k))sum+=c[k];return sum;}
void cdq(int l,int r){
if(l==r)return;
int mid=(l+r)>>1;
cdq(l,mid);cdq(mid+1,r);
int i=l,j=mid+1,k=l,k1=0,k2=0;
while(i<=mid&&j<=r){
if(q[i].time>q[j].time){
updata(q[i].val,1,c1);
ans[q[i].time]+=getsum(q[i].val-1,c2);
t1[++k1]=q[i].val;t[k++]=q[i++];
}else{
updata(q[j].val,1,c2);
ans[q[j].time]+=getsum(n,c1)-getsum(q[j].val,c1);
t2[++k2]=q[j].val;t[k++]=q[j++];
}
}
while(i<=mid){
updata(q[i].val,1,c1);
ans[q[i].time]+=getsum(q[i].val-1,c2);
t1[++k1]=q[i].val;t[k++]=q[i++];
}
while(j<=r){
updata(q[j].val,1,c2);
ans[q[j].time]+=getsum(n,c1)-getsum(q[j].val,c1);
t2[++k2]=q[j].val;t[k++]=q[j++];
}
for(int i=1;i<=k1;i++)updata(t1[i],-1,c1);
for(int i=1;i<=k2;i++)updata(t2[i],-1,c2);
for(int i=l;i<=r;i++)q[i]=t[i];
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)scanf("%d",&a[i]),pos[a[i]]=i;
for(int i=1,x;i<=m;i++){
scanf("%d",&x);
del[pos[x]]=i;
}
int k=m;
for(int i=1;i<=n;i++){
if(!del[i])del[i]=++k;
q[i]=(node){a[i],del[i],i};
}
cdq(1,n);
for(int i=1;i<=n;i++)s+=ans[i];
for(int i=1;i<=m;i++){
printf("%lld\n",s);
s-=ans[i];
}
}
一些实用的函数
const int Inf=... 0x7fffffff是2^31-1,0x3f3f3f3f是10^8的较大数
数组初始化
memset(a,0,sizeof(a));
63较大,64负较大,127很大 -127/128负很大,-1,0
}
next_permutation(a+1,a+n+1); //从小到大生成a数组的全排列(可以自定义优先级)
//生成到最后一个时返回0,使用前先排序
sort(a,a+6); //按从小到大排序 (从大到小的话找的东西相反)
int pos1=lower_bound(a,a+6,x)-num; //返回数组中第一个大于或等于被查数的下标
int pos2=upper_bound(a,a+6,x)-num; //返回数组中第一个大于被查数的下标
1. __gcd(x, y)
求两个数的最大公约数,如__gcd(6, 8) 就返回2。在 algorithm 库中。是不是很方便?
2. reverse(a + 1, a + n + 1)
将数组中的元素反转。a 是数组名,n是长度,跟 sort 的用法一样。值得一提的是,对于字符型数组也同样适用。也在 algorithm 库中。
3. unique(a + 1, a + n + 1)
去重函数。跟sort的用法一样。不过他返回的值是最后一个数的地址,所以要得到新的数组长度应该这么写: _n = unique(a + 1, a + n + 1) - a - 1.
6.fill(a + 1, a + n + 1, x)
将数组a中的每一个元素都赋成x,跟memset的区别是,memset是一个字节一个字节赋值,fill是一个元素一个元素赋值!(省掉一层for……)
数论
· gcd+lcm
int gcd(int a,int b)
{if(!b)return a;else reruen gcd(b,a%b);}
int lcm(int a,int b)
{return a*b/gcd(a,b);}
//algorithm库里有__gcd(a,b)函数可以直接用
· 快速幂
//利用二进制
ll qpow(ll x,ll n,ll mod){
ll ans=1;
while(n){
if(n&1){
ans=ans*x%m;
}
x=x*x%m;
n>>=1;
}
return ans;
· 欧拉筛法
//O(n)
#define N 200000 //范围
int su[N],cnt,u[N]; //u[i]==0是素数,su[]是素数表
void oula(){
for(int i=2;i<N;i++){
if(!u[i])
su[++cnt]=i;
for(int j=1;j<=cnt&&i*su[j]<N;j++){
su[i*su[j]]=1;
if(i%su[j]==0)break; //prime[j]是i的最小质因子
}
}
}
· 判断大数是不是素数(欧拉+sqrt(n)或者Miller算法)
1.欧拉筛法+sqrt(n)判断
#include<bits/stdc++.h>
using namespace std;
#define N 200000 //范围
int su[N],cnt,u[N],n; //u[i]==0是素数,su[]是素数表
void oula();//如上
int isprime(int n){
for(int i=1;i<=cnt;i++){
if(n!=su[i]&&n%su[i]==0)return 0;//不是素数
return 1;
}
}
int main(){
cin>>n;
oula();
int flag=isprime(n);
}
2.Miller-Rabin随机数测试算法
//2^32-1以内,通过2,7,61的miller测试,即为素数
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define M 40001
#define LL long long
LL p,a;
LL su[100001],u[200001],cnt;
void findprime(){
u[1]=1;
for(int i=2;i<=M;i++){
if(!u[i])su[++cnt]=i;
for(int j=1;j<=cnt;j++){
if(i*su[j]>M)break;
u[i*su[j]]=1;
if(i%su[j]==0)break;
}
}
}
int isprime(LL t){
if(t<=M&&!u[t])return 1;
for(int i=1;i<=cnt;i++)if(t%su[i]==0)return 0;
return 1;
}
LL quickpow(LL x,LL p){
LL now=1,o=p;
while(o){
if(o&1)now=(now*x)%p;
x=(x*x)%p;
o>>=1;
}
return now;
}
int main(){
scanf("%lld%lld",&p,&a);
findprime();
while(p!=0||a!=0){
if(isprime(p)){
cout<<"no"<<endl;
scanf("%lld%lld",&p,&a);
continue;
}
LL k=quickpow(a,p);
if(k==a)cout<<"yes"<<endl;
else cout<<"no"<<endl;
scanf("%lld%lld",&p,&a);
}
}
· 扩展欧几里得 (求ax+by=d的整数解)
步骤一:令c=gcd(a,b),如果d%c==0 有解,否则无解
步骤二:a,b,d同除c,得到a`x+b`y=d`
步骤三:用扩欧求a`x`+b`x`=1的解x`,y`,原解x=x`*d`,y=y`*d`
[此时的x,y满足(|x|+|y|)是最小正整数解,x或y都有可能是负数]
若求x的最小正整数解 则x1=(x%b+b)%b, y1=(d-a*x1)/b
若求y的最小正整数解 则y2=(y%a+a)%a, x2=(d-b*y2)/a
若求(x+y)的最小正整数解 则解为min(x1+y1,x2+y2)
扩欧代码:
void ex_gcd(int a,int b,int &x,int &y){
if(b==0){x=1;y=0;return;}
ex_gcd(b,a%b,y,x);
y-=(a/b)*x;
}
· 同余方程
ax≡b(mod m) 两边可以同+、-、* 但不能同/
[若m%gcd(a,b)==0,则a,b,m可同除gcd(a,b) ]
若m%gcd(a,b)==0 则上式恰好有gcd(a,b)个模m不同的解,否则无解
有解时,ax≡b(mod m)的解 等价于 ax+my=b的解,用扩欧求解
· 快速傅里叶变换(FFT)
详解传送门,看不懂@_@,慎点qwq
1.多项式乘法【FFT模板题】传送门
2.A*B problem升级版传送门(数据卡高精乘)
//多项式乘法(FFT)模板
#include<bits/stdc++.h>
using namespace std;
typedef complex<double>Complex;
typedef vector<int>vec;
const double PI=acos(-1.);
const int N=3e6;
int la,lb;
vec w;
template<class T>inline void read(T &x)
{
x=0;int f=0;char ch=getchar();
while(ch<'0'||ch>'9') {f|=(ch=='-');ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x=f?-x:x;
return;
}//快读
void FFT(Complex *P,int n,int op){
for(int i=1,j=0;i<n-1;i++){
for(int s=n;j^=s>>=1,~j&s;);
if(i<j)swap(P[i],P[j]);
}
Complex unit_p0;
for(int d=0;(1<<d)<n;d++){
int m=1<<d,m2=m*2;
double p0=PI/m*op;
unit_p0=Complex(cos(p0),sin(p0));
for(int i=0;i<n;i+=m2){
Complex unit=1;
for(int j=0;j<m;j++){
Complex &P1=P[i+j+m],&P2=P[i+j];
Complex t=unit*P1;//蝴蝶效应,优化
P1=P2-t;
P2=P2+t;
unit=unit*unit_p0;
}
}
}
}
Complex A[N],B[N];
vec operator *(const vec &u,const vec &v){
int n=1,p=u.size(),q=v.size(),i;
while(n<=p+q-2)n<<=1;
for(i=0;i<n;++i)A[i]=i<p?u[i]:0;
for(i=0;i<n;++i)B[i]=i<q?v[i]:0;
FFT(A,n,1);
FFT(B,n,1);
for(i=0;i<n;++i)A[i]*=B[i];
FFT(A,n,-1);
vec w(p+q-1);
for(i=0;i<w.size();++i)
w[i]=(int)(A[i].real()/n+0.5);
return w;
}
vec a,b;
int main(){
cin>>la>>lb;int x;
for(int i=0;i<=la;i++){read(x);a.push_back(x);}
for(int i=0;i<=lb;i++){read(x);b.push_back(x);}
w=a*b;
for(int i=0;i<w.size();i++)printf("%d ",w[i]);
}
· 类欧几里得
注意代码里这个 f(a,b,c,n) 中的 n 是开区间,也就是说计算的是
ll f(ll a,ll b,ll c,ll n) {
if(n<=0)return 0;
return n*(n-1)/2*(a/c)+n*(b/c)+f(c,(a*n+b)%c,a%c,(a%c*n+b%c)/c);
}