Note2

分治

· 归并排序(+求逆序对)

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]);
}

动态逆序对

CQOI2011动态逆序对
在这里插入图片描述

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=...  0x7fffffff2^31-10x3f3f3f3f10^8的较大数
数组初始化  
memset(a,0,sizeof(a));
	63较大,64负较大,127很大 -127/128负很大,-10
}
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);
}
发布了17 篇原创文章 · 获赞 7 · 访问量 2084

猜你喜欢

转载自blog.csdn.net/qq_45530271/article/details/103103334