1.逆序对
#include<bits/stdc++.h>
#define N 100005
using namespace std;
int n,ans,a[N],cur[N];
int merge(int l,int r){
int mid=(l+r)>>1,i=l,j=mid+1,cnt=0;
for(int k=l;k<=r;k++){
if(j>r||i<=mid&&a[i]<a[j]) cur[k]=a[i++];
else cur[k]=a[j++],cnt+=mid-i+1;
}
for(int k=l;k<=r;k++) a[k]=cur[k];
return cnt;
}
int solve(int l,int r){
if(l<r){
int mid=(l+r)>>1;
solve(l,mid),solve(mid+1,r);
ans+=merge(l,r);
}
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
cout<<solve(1,n);
return 0;
}
举个例子
8 5 2 4 6 3 2 9
分
8 5 2 4 与 6 3 2 9
再分
8 5 ,2 4, 6 3, 2 9
治
5 8,ans++ ;2 4;6,3,ans++;2 9
2 4 5 8 ans+=4 ; 2 3 6 9 ans+=2
2 2 3 4 5 6 8 9 ans+=7
ans=14
2.平面上的分治
#include<bits/stdc++.h>
#define N 200005
using namespace std;
int n,inf=0x3fffffff;
struct Node{double x,y;}pos[N],cur[N];
bool cmp1(Node a,Node b){
if(a.x==b.x) return a.y<b.y;
return a.x<b.x;
}
bool cmp2(Node a,Node b){
if(a.y==b.y)return a.x<a.y;
return a.y<b.y;
}
double dis(Node a,Node b){
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double solve(int l,int r){
if(l==r) return inf;
if(l+1==r) return dis(pos[l],pos[r]);
int mid=(l+r)>>1;
double d1=solve(l,mid),d2=solve(mid+1,r);
double d=min(d1,d2);
int tmp=0;
for(int i=l;i<=r;i++){
if(abs(pos[mid].x-pos[i].x)<d)
cur[++tmp]=pos[i];
}
sort(cur+1,cur+tmp+1,cmp2);
for(int i=1;i<=tmp;i++){
for(int j=i+1;j<=tmp&&cur[j].y-cur[i].y<d;j++){
double d3=dis(cur[i],cur[j]);
d=min(d,d3);
}
}
return d;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lf%lf",&pos[i].x,&pos[i].y);
sort(pos+1,pos+n+1,cmp1);
printf("%.4f",solve(1,n));
return 0;
}
x坐标排序
假设画一条线,两边各有n/2个点
现在的最短为min(左边最短,右边最短,左右各选一个最短)
前两个可以继续分治
现在考虑最后一个
假设d=min(左边最短,右边最短)
我们枚举mid-d--mid+d中的点
然后看哪两个<d 然后就可以更新了
(可以先排个序)
3.点分治
给定一棵有n个点的树
询问树上距离=k的点对有多少。
#include<bits/stdc++.h>
#define N 100005
using namespace std;
int first[N],next[N*2],to[N*2],w[N*2],tot;
int n,root,tmp[N],cur[N],ans,vis[N],k;
int S,Size[N],Maxson[N],Max=0x3fffffff,sum;
int read(){
int cnt=0;char ch=0;
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) cnt=cnt*10+(ch-'0'),ch=getchar();
return cnt;
}
void add(int x,int y,int z){
next[++tot]=first[x];
first[x]=tot;
to[tot]=y,w[tot]=z;
}
void getroot(int cur,int fa){
Size[cur]=1;
for(int i=first[cur];i;i=next[i]){
int t=to[i];
if(t==fa||vis[t]) continue;
getroot(t,cur);
Size[cur]+=Size[t];
Maxson[cur]=max(Maxson[cur],Size[t]);
}
Maxson[cur]=max(Maxson[cur],S-Size[cur]);
//不为根节点时,除去它的子树,上面还有一坨
if(Maxson[cur]<Max&&Maxson[cur]!=-1) root=cur,Max=Maxson[cur];
}
void getdis(int cur,int fa,int dis){
tmp[++sum]=dis;
for(int i=first[cur];i;i=next[i]){
int t=to[i];
if(t==fa||vis[t]) continue;
getdis(t,cur,dis+w[i]);
}
}
int solve(int x,int d){
sum=0;
getdis(x,0,d);
sort(tmp+1,tmp+sum+1);
int l=1,r=sum,res=0;
while(l<=r){
if(tmp[l]+tmp[r]<=k) res+=r-l,l++;
else r--;
}
return res;
}
void getans(int x){
ans+=solve(x,0);
vis[x]=1;
for(int i=first[x];i;i=next[i]){
int t=to[i];
if(vis[t]) continue;
ans-=solve(t,w[i]);
S=Size[t],root=0;
Max=0x3fffffff,getroot(t,0);//Maxson不会变大
getans(root);
}
}
int main()
{
//freopen("1.in","r",stdin);
n=read(),k=read();
for(int i=1;i<n;i++){
int x=read(),y=read(),z=read();
add(x,y,z),add(y,x,z);
}
memset(Maxson,-1,sizeof(Maxson));
getroot(1,0);
getans(root);//分治
cout<<ans;
return 0;
}
三步走
1.找重心(getroot)
2.处理经过重心的
3.没经过的丢给儿子,重复1
注意这句
ans-=solve(t,w[i]);
找到两条经过root的路径后,有可能两条在同一棵子树,所以要去掉