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
#include
#include
#include
#include
#include
#include
#include
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);
}
引例:二维偏序
三维偏序模板题:陌上花开
思路:
对第一维排序,对第二维归并排序,第三维用树状数组维护
[l,mid]放树状数组里,[mid+1,r]计算前面对后面的贡献
!要预处理相同的三元组后面对前面的贡献(因为cdq分治的时候后面的三元组不会对前面的三元组产生贡献)
//洛谷P3810陌上花开
#include
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]
#include
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));//int
63较大,-64负较大,127很大 -127/128负很大,-1,0
}
next_permutation(a+1,a+n+1); //从小到大生成a数组的全排列(可以自定义优先级)
//生成到最后一个时返回0,使用前先排序
random_shuffle(a+1,a+1+n);//随机排列,打乱顺序
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……)
对a[n]数组离散化,O(nlogn)
#include
using namespace std;
const int N=100007;
int n,a[N],A[N];
int main(){
cin>>n;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
//离散化
for(int i=1;i<=n;i++)A[i]=a[i];
sort(A+1,A+1+n);
int size=unique(A+1,A+1+n)-A-1;
for(int i=1;i<=n;i++)a[i]=lower_bound(A+1,A+1+size,a[i])-A;
//
for(int i=1;i<=n;i++)printf("%d ",a[i]);
}
int gcd(int a, int b){
while (b)
b ^= a ^= b ^= a %= b;
return a;
}
int lcm(int a,int b)
{return a*b/gcd(a,b);}
//algorithm库里有__gcd(a,b)函数可以直接用
LL qmul(LL a,LL b){
LL re=0;
while(b){
if(b&1)re=(re+a)%MOD;
a=(a+a)%MOD;
b>>=1;
}
return re;
}
注意:当mod为质数时,对指数e取余是%(mod-1),然后取qpow(x,e+mod-1)为结果 [费马小定理]
LL qpow(LL x,LL e){
LL re=1;
while(e){
if(e&1){
re=re*x%MOD;
}
x=x*x%MOD;
e>>=1;
}
return re%MOD;
}
/* 分解质因数 */
void Solve(LL n) {
p.clear();
for(LL i=2;i*i<=n;i++){
if(n%i==0){
p.push_back(i);
while(n%i==0) n/=i;
}
}
if(n>1)
p.push_back(n); //这个不可以缺少
}
然后,n的因子个数等于(p[0]+1) * (p[1]+1) * (p[2]+1) * … * (p[k]+1)
O(log2n)
LL cal(LL p,LL n){ ///这里是递归求解等比数列模板 1 + p + p^2 +...+ p^n
if(n==0) return 1;
if(n&1){///(1+p+p^2+....+p^(n/2))*(1+p^(n/2+1));
return (1+qpow(p,n/2+1))*cal(p,n/2)%MOD;
}
else { ///(1+p+p^2+....+p^(n/2-1))*(1+p^(n/2+1))+p^(n/2);
return (qpow(p,n/2)+(1+qpow(p,n/2+1))*cal(p,n/2-1))%MOD;
}
}
k个不同的数,每个数a1,a2,…,ak个,不同的排列数为
( a 1 + a 2 + . . . + a k ) ! a 1 ! ∗ a 2 ! ∗ . . . ∗ a k ! \frac{(a1+a2+...+ak)!}{a1!*a2!*...*ak!} a1!∗a2!∗...∗ak!(a1+a2+...+ak)!
LL solve(LL n,LL k){// [n/1]+[n/2]+[n/3]+...+[n/k] []向下取整
LL re=0;
for(LL l=1,r;l<=min(n,k);l=r+1){
r=min(n/(n/l),k);
re=(re+n/l%MOD*((r-l+1)%MOD))%MOD;
}
return re;
}
//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++){
u[i*su[j]]=1;
if(i%su[j]==0)break; //prime[j]是i的最小质因子
}
}
}
LL qmul(LL a,LL b,LL MOD){
LL re=0;
while(b){
if(b&1)re=(re+a)%MOD;
a=(a+a)%MOD;
b>>=1;
}
return re;
}
LL qpow(LL x,LL e,LL MOD){
LL re=1;
while(e){
if(e&0x01){
re=qmul(re,x,MOD);
}
x=qmul(x,x,MOD);
e>>=1;
}
return re%MOD;
}
bool Miller_Rabin(LL x){//判断x是不是素数,是返回1
if(x==2) return 1;
if(!(x&1)||x==1) return 0;
bool pass;
LL d=x-1,m;
while(!(d&1)) d>>=1;LL tmp=d;
for(int i=1;i<=10;++i){//随机数检验10次基本上能确定,longlong以内可以用2,3,5,7,11,13,17,19检验,出错率0%
d=tmp;pass=0;
m=qpow(rand()%(x-2)+2,d,x);
if(m==1) continue;else
for(;d<x&&d>=0;m=qmul(m,m,x),d<<=1)
if(m==x-1){pass=1;break;}
if(!pass) return 0;
}
return 1;
}
p为素数当且仅当(p-1)! ≡ p-1 (mod p)
步骤一:令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(LL a,LL b,LL &x,LL &y){
if(b==0){x=1;y=0;return;}
ex_gcd(b,a%b,y,x);
y-=(a/b)*x;
}
若mod为质数,a的逆元为qpow(a,p-2)
LL qpow(LL x,LL e){
LL re=1;
while(e){
if(e&1){
re=re*x%MOD;
}
x=x*x%MOD;
e>>=1;
}
return re%MOD;
}
否则如下
void ex_gcd(LL a,LL b,LL &x,LL &y){
if(b==0){x=1;y=0;return;}
ex_gcd(b,a%b,y,x);
y-=(a/b)*x;
}
LL inverse(LL a,LL b){//求a模b的逆元 [a,b互质才有解]
LL x,y;
ex_gcd(a,b,x,y);
return x=(x%b+b)%b;
}
LL fac[N];
void init(){fac[0]=fac[1]=1LL;for(LL i=1;i<N;i++)fac[i]=fac[i-1]*i%MOD;}
LL qpow(LL x,LL e){
LL re=1;
while(e){
if(e&1){
re=re*x%MOD;
}
x=x*x%MOD;
e>>=1;
}
return re%MOD;
}
LL C(LL n,LL m){
LL re,up,down;
up=fac[n];
down=fac[m]*fac[n-m]%MOD;
re=up*qpow(down,MOD-2)%MOD;
return re;
}
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的解,用扩欧求解
详解传送门,看不懂@_@,慎点qwq
1.多项式乘法【FFT模板题】传送门
2.A*B problem升级版传送门(数据卡高精乘)
//多项式乘法(FFT)模板
#include
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]);
}
例题链接:[洛谷 4132 算不出的不等式]
注意代码里这个 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);
}