离线分治算法:

一、基于时间的分治算法 --------CDQ分治
例题:AcWing 254. 天使玩偶
Ayu在七年前曾经收到过一个天使玩偶,当时她把它当做时间囊埋在了地下。

而七年后的今天,Ayu却忘了她把天使玩偶埋在了哪里,所以她决定仅凭一点模糊的记忆来寻找它。

我们把Ayu生活的小镇看做一个二维平面直角坐标系,而Ayu会不定时的记起可能在某个点(x,y)埋下了天使玩偶。

或者Ayu会询问你,假如她在(x,y),那么她离最近的天使玩偶可能埋下的地方有多远。

因为Ayu只会沿着平行坐标轴的方向来行动,所以在这个问题里我们定义两个点之间的距离为曼哈顿距离:

dist(A,B)=|Ax−Bx|+|Ay−By|
其中Ax表示点A的横坐标,其余类似。

输入格式
第一行包含两个整数n和m,在刚开始时,Ayu已经知道有n个点可能埋着天使玩偶,接下来Ayu要进行m次操作。

接下来n行,每行两个非负整数xi,yi,表示初始n个点的坐标。

再接下来m行,每行三个非负整数 t,x,y 。

如果t=1,表示Ayu又回忆起了一个可能埋着玩偶的点(x,y)。

如果t=2,表示Ayu询问如果她在坐标(x,y),那么在已经回忆出的点里,离她最近的那个点有多远。

输出格式
对于每个t=2的询问,在单独的一行内输出该询问的结果。

数据范围
n,m≤5∗105,坐标范围为 0~106。

输入样例:
2 3
1 1
2 3
2 1 2
1 3 3
2 4 2
输出样例:
1
2

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#define ll long long
#define llu unsigned ll
using namespace std;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int maxn=1000100;
struct node
{
    int pos,x,y;
};
node a[maxn];//原始问题的操作序列,长度为n+m
node b[maxn];//静态问题的坐标,按横坐标排序,及其在a中的下标
int c[maxn],tot;//树状数组,坐标的最大范围
int ans[maxn],n,m,t;

bool operator <(const node &a,const node &b)
{
    if(a.x!=b.x) return a.x<b.x;
    else return a.y<b.y;
}

int ask(int x)
{
    int ans=-inf;
    for(;x;x-=(x&-x))
        ans=max(ans,c[x]);
    return ans;
}

void add(int x,int y)
{
    for(x;x<tot;x+=(x&-x)) c[x]=max(c[x],y);
}

// 求解简化版问题,需要考虑b[st~ed]的坐标,根据4个方向的不同,
// 横坐标顺序为de(±1),树状数组维护的信息用系数dx,dy(±1)指定

void cal(int st,int ed,int de,int dx,int dy)
{
    for(int i=st;i!=ed;i+=de)
    {
        int y=(dy==1?b[i].y:tot-b[i].y);
        int temp=dx*b[i].x+dy*b[i].y;
        if(a[b[i].pos].pos==1) add(y,temp);
        else ans[b[i].pos]=min(ans[b[i].pos],abs(temp-ask(y)));
    }
    //撤销修改。
    for(int i=st;i!=ed;i+=de)
    {
        int y=(dy==1?b[i].y:tot-b[i].y);
        if(a[b[i].pos].pos==1)
            for(int j=y;j<tot;j+=(j&-j)) c[j]=-inf;
    }
}



void cdq_div(int l,int r)
{
    if(l==r) return ;
    int mid=(l+r)>>1;
    if(l<=mid) cdq_div(l,mid);
    if(mid+1<=r) cdq_div(mid+1,r);

    t=0;
    for(int i=l;i<=r;i++)
    {
        if(i<=mid&&a[i].pos==1||i>mid&&a[i].pos==2)
            b[++t]=a[i],b[t].pos=i;
    }
    // 此处排序可以优化掉(放在外边并适当修改写法)
    sort(b+1,b+t+1);
    cal(1,t+1,1,1,1);
    cal(1,t+1,1,1,-1);
    cal(t,0,-1,-1,-1);
    cal(t,0,-1,-1,1);
}


int main(void)
{
    scanf("%d%d",&n,&m);
    m+=n;

    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&a[i].x,&a[i].y);
        a[i].y++;
        a[i].pos=1,
        tot=max(tot,a[i].y);
    }

    for(int i=n+1;i<=m;i++)
    {
        scanf("%d%d%d",&a[i].pos,&a[i].x,&a[i].y);
        a[i].y++;
        tot=max(tot,a[i].y);
    }
    tot++;
    memset(c,0x80,sizeof(c));
    memset(ans,0x3f,sizeof(ans));
    cdq_div(1,m);

    for(int i=1;i<=m;i++)
    {
        if(a[i].pos==2)
            printf("%d\n",ans[i]);
    }

    return 0;

}

二、基于值域的整体分治算法:

我又把这道题目搬出来了。。。
例①:K-th Number POJ - 2104
You are working for Macrohard company in data structures department. After failing your previous task about key insertion you were asked to write a new data structure that would be able to return quickly k-th order statistics in the array segment.
That is, given an array a[1…n] of different integer numbers, your program must answer a series of questions Q(i, j, k) in the form: “What would be the k-th number in a[i…j] segment, if this segment was sorted?”
For example, consider the array a = (1, 5, 2, 6, 3, 7, 4). Let the question be Q(2, 5, 3). The segment a[2…5] is (5, 2, 6, 3). If we sort this segment, we get (2, 3, 5, 6), the third number is 5, and therefore the answer to the question is 5.
Input
The first line of the input file contains n — the size of the array, and m — the number of questions to answer (1 <= n <= 100 000, 1 <= m <= 5 000).
The second line contains n different integer numbers not exceeding 10 9 by their absolute values — the array for which the answers should be given.
The following m lines contain question descriptions, each description consists of three numbers: i, j, and k (1 <= i <= j <= n, 1 <= k <= j - i + 1) and represents the question Q(i, j, k).
Output
For each question output the answer to it — the k-th number in sorted a[i…j] segment.
Sample Input
7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3
Sample Output
5
6
3
Hint
This problem has huge input,so please use c-style input(scanf,printf),or you may got time limit exceed.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<deque>
#include<map>
#include<vector>
#include<cmath>
#define ll long long
#define llu unsigned ll
using namespace std;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int maxn=101000;
const int mod=1e9+7;
struct node
{
    int pos;
    int x,y;
    int z;
    node(){}
    node(int a,int b,int c,int d)
    {
        pos=a,x=b,y=c,z=d;
    }
}q[maxn<<1],lq[maxn<<1],rq[maxn<<1];

int n,m,t;
int sum[maxn],ans[maxn];

void add(int x,int val)
{
    for(;x<maxn;x+=(x&(-x)))
        sum[x]+=val;
}

int ask(int x)
{
    int ans=0;
    for(;x;x-=(x&-x))
        ans+=sum[x];
    return ans;
}

void solve(int lval,int rval,int st,int ed)
{
    if(st>ed) return ;
    if(lval==rval)
    {
        for(int i=st;i<=ed;i++)
            if(q[i].pos) ans[q[i].pos]=lval;
        return ;
    }

    int mid=(lval+rval)>>1;
    int lcnt=0,rcnt=0;
    for(int i=st;i<=ed;i++)
    {
        if(q[i].pos==0)
        {
            if(q[i].z<=mid) add(q[i].x,1),lq[++lcnt]=q[i];
            else rq[++rcnt]=q[i];
        }
        else
        {
            int cnt=ask(q[i].y)-ask(q[i].x-1);
            if(cnt>=q[i].z) lq[++lcnt]=q[i];
            else q[i].z-=cnt,rq[++rcnt]=q[i];
        }
    }

    for(int i=st;i<=ed;i++)
        if(q[i].pos==0&&q[i].z<=mid) add(q[i].x,-1);

    for(int i=1;i<=lcnt;i++) q[st+i-1]=lq[i];
    for(int i=1;i<=rcnt;i++) q[st+lcnt+i-1]=rq[i];
    solve(lval,mid,st,st+lcnt-1);
    solve(mid+1,rval,st+lcnt,ed);
}

int main(void)
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(sum,0,sizeof(sum));
        int tot=0;
        int x,y,z;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&x);
            q[++tot]=node(0,i,0,x);
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            q[++tot]=node(i,x,y,z);
        }

        solve(-inf,inf,1,tot);

        for(int i=1;i<=m;i++)
        {
            printf("%d\n",ans[i]);
        }
    }
    return 0;
}

例②:ZOJ 2112
Dynamic Rankings

Time Limit: 10000 msMemory Limit: 32768 KB
The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with the query like to simply find the k-th smallest number of the given N numbers. They have developed a more powerful system such that for N numbers a[1], a[2], …, a[N], you can ask it like: what is the k-th smallest number of a[i], a[i+1], …, a[j]? (For some i<=j, 0<k<=j+1-i that you have given to it). More powerful, you can even change the value of some a[i], and continue to query, all the same.

Your task is to write a program for this computer, which

  • Reads N numbers from the input (1 <= N <= 50,000)

  • Processes M instructions of the input (1 <= M <= 10,000). These instructions include querying the k-th smallest number of a[i], a[i+1], …, a[j] and change some a[i] to t.

Input

The first line of the input is a single number X (0 < X <= 4), the number of the test cases of the input. Then X blocks each represent a single test case.

The first line of each block contains two integers N and M, representing N numbers and M instruction. It is followed by N lines. The (i+1)-th line represents the number a[i]. Then M lines that is in the following format

Q i j k or
C i t

It represents to query the k-th number of a[i], a[i+1], …, a[j] and change some a[i] to t, respectively. It is guaranteed that at any time of the operation. Any number a[i] is a non-negative integer that is less than 1,000,000,000.

There’re NO breakline between two continuous test cases.

Output

For each querying operation, output one integer to represent the result. (i.e. the k-th smallest number of a[i], a[i+1],…, a[j])

There’re NO breakline between two continuous test cases.

Sample Input

2
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3

Sample Output

3
6
3
6

带修改的区间第K小。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<deque>
#include<map>
#include<vector>
#include<cmath>
#define ll long long
#define llu unsigned ll
using namespace std;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int maxn=101000;
const int mod=1e9+7;
struct node
{
    int pos,x,y,z;
    node(){}
    node(int a,int b,int c,int d)
    {
        pos=a,x=b,y=c,z=d;
    }
}q[maxn*3],lq[maxn*3],rq[maxn*3];
int n,m,t,p,ans[maxn],sum[maxn],a[maxn];
char pos[20];

void add(int x,int y)
{
    for(;x<maxn;x+=(x&-x))
        sum[x]+=y;
}

int ask(int x)
{
    int ans=0;
    for(;x;x-=(x&-x))
        ans+=sum[x];
    return ans;
}

void solve(int lval,int rval,int st,int ed)
{
    if(st>ed) return ;
    if(lval==rval)
    {
        for(int i=st;i<=ed;i++)
            if(q[i].pos>0) ans[q[i].pos]=lval;
        return ;
    }

    int mid=(lval+rval)>>1;
    int lt=0,rt=0;
    for(int i=st;i<=ed;i++)
    {
        if(q[i].pos==0)
        {
            if(q[i].y<=mid) add(q[i].x,q[i].z),lq[++lt]=q[i];
            else rq[++rt]=q[i];
        }
        else
        {
            int cnt=ask(q[i].y)-ask(q[i].x-1);
            if(cnt>=q[i].z) lq[++lt]=q[i];
            else q[i].z-=cnt,rq[++rt]=q[i];
        }
    }
    for(int i=st;i<=ed;i++)
    {
        if(q[i].pos<=0&&q[i].y<=mid) add(q[i].x,-q[i].z);
    }
    for(int i=1;i<=lt;i++) q[st+i-1]=lq[i];
    for(int i=1;i<=rt;i++) q[st+lt+i-1]=rq[i];
    solve(lval,mid,st,st+lt-1);
    solve(mid+1,rval,st+lt,ed);

}

int main(void)
{
    int tt;
    scanf("%d",&tt);
    while(tt--)
    {
        int x,y,z;
        t=p=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&y);
            q[++t]=node(0,i,y,1);
            a[i]=y;
        }

        for(int i=1;i<=m;i++)
        {
            scanf("%s",pos);
            if(pos[0]=='Q')
            {
                scanf("%d%d%d",&x,&y,&z);
                q[++t]=node(++p,x,y,z);
            }
            else
            {
                scanf("%d%d",&x,&y);
                q[++t]=node(0,x,a[x],-1);
                q[++t]=node(0,x,y,1);
                a[x]=y;
            }
        }
        solve(0,inf,1,t);
        for(int i=1;i<=p;i++)
        {
            printf("%d\n",ans[i]);
        }
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值