CDQ分治

分治及练习题

前置知识

参考如下博客:

五大常用算法:一文搞懂分治算法

1. 最大子段和

传送门

#include <iostream>
#include<algorithm>
using namespace std;
const int N = 2e5 + 5;
const int INF = 0x7ffffff;
int n;
int a[N];
int solve(int L,int R){
    if(L==R) return a[L];
    int mid=L+R>>1;
    int lans=solve(L,mid),rans=solve(mid+1,R);
    int maxsuf=-INF,maxpre=-INF,suf=0,pre=0;
    for(int i=mid;i>=L;i--){
        suf+=a[i];
        maxsuf=max(maxsuf,suf);
    }
    for(int i=mid+1;i<=R;i++){
        pre+=a[i];
        maxpre=max(maxpre,pre);
    }
    return max(max(lans,rans),maxsuf+maxpre);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    printf("%d",solve(1,n));
    return 0;
}

2. 快速排序

传送门

#include <iostream>
#include<algorithm>
using namespace std;
const int N = 2e5 + 5;
const int INF = 0x7ffffff;
int n;
int a[N],tmp[N];
void fast_sort(int L,int R){
    if(L==R) return;
    int mid=L+R>>1;
    fast_sort(L,mid);
    fast_sort(mid+1,R);
    int i=L,j=mid+1,k=L;
    while(i<=mid&&j<=R){
        if(a[i]<a[j]) tmp[k++]=a[i++];
        else tmp[k++]=a[j++];
    }
    while(i<=mid) tmp[k++]=a[i++];
    while(j<=R) tmp[k++]=a[j++];
    for(int i=L;i<=R;i++){
        a[i]=tmp[i];
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    fast_sort(1,n);
    for(int i=1;i<=n;i++){
        printf("%d ",a[i]);
    }
    return 0;
}

3. 平面上的最接近点对

传送门

暴力法

#include<iostream>
#define ll long long
using namespace std;
template < class T > inline void read(T &x)
{
    x = 0;
    char c = getchar();
    bool f = 0;
    for (; !isdigit(c); c = getchar()) f ^= c == '-';
    for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48);
    x = f ? -x : x;
}
template < class T > inline void write(T x)
{
    if (x < 0) putchar('-'), x = -x;
    if (x > 9) write(x / 10);
    putchar(x % 10 + 48);
}
const ll N=10000+5;
const ll INF=0x7fffffff;
ll n;
ll ans=INF;
struct node
{
    ll x,y;
}dian[N];
ll dis(ll x1,ll x2,ll y1,ll y2){
    return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
}
int main()
{
    ios::sync_with_stdio(false);
    read(n);
    for(int i=1;i<=n;++i)
    {
        read(dian[i].x);
        read(dian[i].y);
    }
    for(int i=1;i<=n;++i)
    {
        for(int j=i+1;j<=n;++j)
        {
            ans=min(dis(dian[i].x,dian[j].x,dian[i].y,dian[j].y),ans);//暴力枚举并维护
        }
    }
    printf("%0.4lf",(double)sqrt(ans));
    return 0;
}

分治法

在这里插入图片描述

完整程序

#include<bits/stdc++.h>
using namespace std;
struct point
{
    double x,y;
}p[200010];
int n,temp[200010];
bool cmp(const point &A,const point &B)
{
    if(A.x==B.x)
        return A.y<B.y;
    else
        return A.x<B.x;
}
bool cmps(const int &a,const int &b)
{
    return p[a].y<p[b].y;
}
double distance(int i,int j)
{
    return sqrt((p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y));
}
double merge(int left,int right)
{
    double dis=2<<20;
    if(left==right)
        return dis;
    if(left+1==right)
        return distance(left,right);
    int mid=(left+right)>>1;
    double d1=merge(left,mid);
    double d2=merge(mid+1,right);
    dis=min(d1,d2);
    int k=0;
    for(int i=left;i<=right;i++)
        if(fabs(p[i].x-p[mid].x)<=dis)
            temp[k++]=i;
    sort(temp,temp+k,cmps);
    for(int i=0;i<k;i++)
        for(int j=i+1;j<k&&p[temp[j]].y-p[temp[i]].y<dis;j++)
            dis=min(dis,distance(temp[i],temp[j]));
    return dis; 
}
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
        scanf("%lf %lf",&p[i].x,&p[i].y);
    sort(p,p+n,cmp);
    printf("%.4lf\n",merge(0,n-1));
    return 0;
}

4. L国的战斗之排兵布阵

传送门

#include<cstdio>
using namespace std;
#define MAXN 1050

int map[MAXN][MAXN];

bool visit[MAXN][MAXN];

int n,dx,dy;

int s=1;

int flag=0;

int cnt=1,cc=0;

//deep dark fantasy 原版分治 
//cc为原始编码序号 
void ddf(int a,int b,int c,int d,int e){
    if(c==1) return;
    c/=2;
    if(d<a+c&&e<b+c)//特殊点在左上角 
    {
        ddf(a,b,c,d,e);
        ddf(a,b+c,c,a+c-1,b+c);
        ddf(a+c,b,c,a+c,b+c-1);
        ddf(a+c,b+c,c,a+c,b+c);
        //分治 
        cc++;
        map[a+c][b+c]=cc;
        map[a+c][b+c-1]=cc;
        map[a+c-1][b+c]=cc;
        //添加一个L 
    }
    if(d<a+c&&e>=b+c)//特殊点在右上角 
    {
        ddf(a,b,c,a+c-1,b+c-1);
        ddf(a,b+c,c,d,e);

        ddf(a+c,b,c,a+c,b+c-1);
        ddf(a+c,b+c,c,a+c,b+c);

        cc++;
        map[a+c][b+c]=cc;
        map[a+c][b+c-1]=cc;
        map[a+c-1][b+c-1]=cc;
    }
    if(d>=a+c&&e<b+c)//特殊点在左下角 
    {
        ddf(a,b,c,a+c-1,b+c-1);
        ddf(a,b+c,c,a+c-1,b+c);    

        ddf(a+c,b,c,d,e);
        ddf(a+c,b+c,c,a+c,b+c);

        cc++;
        map[a+c-1][b+c-1]=cc;
        map[a+c-1][b+c]=cc;
        map[a+c][b+c]=cc;
    }
    if(d>=a+c&&e>=b+c)//特殊点在右下角  
    {
        ddf(a,b,c,a+c-1,b+c-1);
        ddf(a,b+c,c,a+c-1,b+c);

        ddf(a+c,b,c,a+c,b+c-1);
        ddf(a+c,b+c,c,d,e);

        cc++;
        map[a+c-1][b+c-1]=cc;
        map[a+c][b+c-1]=cc;
        map[a+c-1][b+c]=cc;
    }
}
//BFS暴力重编码 
void dnum(int x,int y){
    map[x][y]=cnt;
    visit[x][y]=1;
    if(!visit[x+1][y]&&map[x+1][y]==flag) dnum(x+1,y);
    if(!visit[x-1][y]&&map[x-1][y]==flag) dnum(x-1,y);
    if(!visit[x][y+1]&&map[x][y+1]==flag) dnum(x,y+1);
    if(!visit[x][y-1]&&map[x][y-1]==flag) dnum(x,y-1);
}

int main(){
    scanf("%d%d%d",&n,&dx,&dy);
    while(n){
        s*=2;
        n--;
    }//s=2^k
    
    ddf(1,1,s,dx,dy);//分治 
    
    visit[dx][dy]=1;
    for(int i=1;i<=s;i++){
        for(int j=1;j<=s;j++){
            if(visit[i][j]){
                printf("%d ",map[i][j]);
                continue;
            }
            flag=map[i][j];
            dnum(i,j);
            cnt++;
            printf("%d ",map[i][j]);
        }
        putchar('\n');
    }//BFS暴力重编码并输出 
    
    return 0;
}

5. 逆序对(归并排序)

传送门

#include <iostream>
#include<algorithm>
using namespace std;
#define int long long
const int N = 5e5 + 5;
const int INF = 0x7ffffff;
int n;
int a[N],tmp[N];
template <typename T>
inline void read(T &x) {
    register T c = getchar();
    for (; c < 48 || 57 < c; c = getchar())
        ;
    for (; 48 <= c && c <= 57; c = getchar())
        x = (x << 3) + (x << 1) + (c & 15);
}
template <typename T>
inline void print(T x) {
    if (x > 9)
        print(x / 10);
    putchar(x % 10 | 48);
}
int CDQ(int L,int R){
    if(L==R) return 0;
    int mid=L + R >>1;
    int ans=CDQ(L,mid)+CDQ(mid+1,R);
    int i=L,j=mid+1,k=L;
    while(i<=mid&&j<=R){
        if(a[i]<=a[j]) tmp[k++]=a[i++];
        else tmp[k++]=a[j++],ans+=mid-i+1;
    }
    while(i<=mid) tmp[k++]=a[i++];
    while(j<=R) tmp[k++]=a[j++];
    for(int i=L;i<=R;i++){
        a[i]=tmp[i];
    }
    return ans;
}
signed main(){
    read(n);
    for(int i=1;i<=n;i++){
        read(a[i]);
    }
    print(CDQ(1,n));
    return 0;
}

6. gpx不会的题

传送门

ST表

#include <bits/stdc++.h>
#define loc(x, y) ((x - 1) * m + y)
#define lowbit(x) (x & -x)
using namespace std;
const int N = 6e5 + 5, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double PI = acos(-1), EPS = 1e-8;
typedef long long ll;
namespace fast_IO {
    ll read() {
        ll num = 0;
        char c;
        bool tag = false;
        while ((c = getchar()) != '-' && c != '+' && (c < '0' || c > '9') && ~c);
        if (!~c)return EOF;
        if (c == '-')tag = true;
        else if (c == '+')tag = false;
        else num = c ^ 48;
        while ((c = getchar()) >= '0' && c <= '9')
            num = (num << 1) + (num << 3) + (c ^ 48);
        if (tag)return -num;
        return num;
    }
}
int a[N], b[N], STa[N][17], STb[N][17];
int query(int a[], int ST[][17], int l, int r) {
    int k = log2(r - l + 1);
    if (a[ST[l][k]] <= a[ST[r - (1 << k) + 1][k]])return ST[l][k];
    return ST[r - (1 << k) + 1][k];
}
bool judge(int l, int r) {
    if (l >= r)return true;
    int mina = query(a, STa, l, r), minb = query(b, STb, l, r);
    if (mina != minb)return false;
    return judge(l, mina - 1) & judge(mina + 1, r);
}

int main() {
    int n;
    while (~scanf("%d", &n)) {
        for (int i = 1; i <= n; ++i)a[i] = fast_IO::read();
        for (int i = 1; i <= n; ++i)b[i] = fast_IO::read();
        for (int i = 1; i <= n; ++i)STa[i][0] = i, STb[i][0] = i;
        for (int j = 1; j < 17; ++j) {
            for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
                if (a[STa[i][j - 1]] <= a[STa[i + (1 << j - 1)][j - 1]])
                    STa[i][j] = STa[i][j - 1];
                else STa[i][j] = STa[i + (1 << j - 1)][j - 1];
                if (b[STb[i][j - 1]] <= b[STb[i + (1 << j - 1)][j - 1]])
                    STb[i][j] = STb[i][j - 1];
                else STb[i][j] = STb[i + (1 << j - 1)][j - 1];
            }
        }
        int l = 1, r = n;
        while (l <= r) {
            int mid = l + r >> 1;
            if (judge(1, mid))l = mid + 1;
            else r = mid - 1;
        }
        printf("%d\n", r);
    }
    return 0;
}

7. [USACO04OPEN]MooFest

传送门

AC

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
#define int long long
const int N = 5e4 + 5;
int n;
int sum[N];
struct node{
    int v,x;
}a[N];
bool cmp1(node x,node y){
    if(x.v==y.v)
        return x.x<y.x;
    return x.v<y.v;
}
bool cmp2(node x, node y){
    return x.x<y.x;
}
int solve(int l,int r){
    if(l==r) return 0;
    int mid = (l + r)>>1;
    int ans=solve(l, mid)+solve(mid + 1, r);
    sum[l-1]=0;
    sort(a+l,a+mid+1,cmp2);
    for (int i = l; i <= r; i++) sum[i] = sum[i - 1] + a[i].x;
    int i = l - 1;
    sort(a+mid+1,a+r+1,cmp2);
    for (int j = mid + 1; j <= r; j++) {
        while (a[i + 1].x <= a[j].x && i < mid)
            i++;
        ans += a[j].v * ((i - l + 1) * a[j].x - sum[i] + sum[mid] - sum[i] - (mid - i) * a[j].x);
    }
    return ans;
}
signed main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld %lld",&a[i].v,&a[i].x);
    sort(a+1,a+1+n,cmp1);
    printf("%lld",solve(1,n));
    return 0;
}

8. 寒假作业(推公式+归并排序)

传送门

在这里插入图片描述

#include <iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
const int INF = 0x7ffffff;
int n,k;
int a[N],tmp[N];
template <typename T>
inline void read(T &x) {
    register T c = getchar();
    for (; c < 48 || 57 < c; c = getchar())
        ;
    for (; 48 <= c && c <= 57; c = getchar())
        x = (x << 3) + (x << 1) + (c & 15);
}
template <typename T>
inline void print(T x) {
    if (x > 9)
        print(x / 10);
    putchar(x % 10 | 48);
}
ll CDQ(int L,int R){
    if(L==R) return 0;
    int mid=L + R >>1;
    ll ans=CDQ(L,mid)+CDQ(mid+1,R);
    int i=L,j=mid+1,k=L;
    ll sum=0;
    while(k<=R){
        if(j>R || i<=mid && a[i]<=a[j]) tmp[k++]=a[i++],sum++;
        else ans+=sum,tmp[k++]=a[j++];
    }
    while(i<=mid) tmp[k++]=a[i++];
    while(j<=R) tmp[k++]=a[j++];
    for(int i=L;i<=R;i++){
        a[i]=tmp[i];
    }
    return ans;
}
int main(){
    read(n);read(k);
    for(int i=1;i<=n;i++){
        read(a[i]);
        a[i]+=a[i-1];
    }
    for(int i=1;i<=n;i++)
        a[i]-=k*i;
    print(CDQ(0,n));
    return 0;
}

9. 三维偏序(树状数组+分治)

传送门

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100005;
const int maxm = 200005;
int n, m, kk;
int sum[maxm], p[maxm];
int lowbit(int x){ return x & (-x); }
void update(int x,int k){ for (int i = x; i <= kk; i+=lowbit(i)) sum[i] += k; }
int q(int x){ int ans = 0; for (int i = x; i != 0; i-=lowbit(i)) ans += sum[i]; return ans; }

struct node{
    int x, y, z, w, ans;
} a[maxn], b[maxn];
bool rulex(node x,node y)
{
    if(x.x==y.x)
    {
        if(x.y==y.y)
            return x.z < y.z;
        return x.y < y.y;
    }
    return x.x < y.x;
}
bool ruley(node x,node y)
{
    if(x.y==y.y)
        return x.z < y.z;
    return x.y < y.y;
}

void slove(int l,int r)
{
    if(l==r)
        return;
    int mid = (l + r) / 2;
    int i = l, j = mid + 1;
    slove(l, mid);
    slove(mid + 1, r);
    sort(a + l, a + mid + 1, ruley);
    sort(a + mid + 1, a + r + 1, ruley);
    while(j<=r)
    {
        while(a[j].y>=a[i].y&&i<=mid)
            update(a[i].z, a[i].w), i++;
        a[j].ans += q(a[j].z);
        j++;
    }
    for (j = l; j < i; j++)
        update(a[j].z, -a[j].w);
}

int main()
{
    scanf("%d%d", &m, &kk);
    for (int i = 1; i <= m; i++)
        scanf("%d%d%d", &b[i].x, &b[i].y, &b[i].z);
    sort(b + 1, b + 1 + m, rulex);
    int cnt = 0, n = 0;
    for (int i = 1; i <= m; i++)
    {
        cnt++;
        if(b[i].x!=b[i+1].x||b[i].y!=b[i+1].y||b[i].z!=b[i+1].z)
            a[++n] = b[i], a[n].w = cnt, cnt = 0;
    }
    slove(1, n);
    for (int i = 1; i <= n; i++)
        p[a[i].ans + a[i].w - 1] += a[i].w;
    for (int i = 0; i < m; i++)
        printf("%d\n", p[i]);

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeeGLMath

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值