AcWing - 2016 年清华大学计算机系推研 - 1327. 魔法学校

在一所魔法学校中,有许多的学生,每个学生有一个学号(从 1 开始,用连续的正整数编号)。

每天,这所学校的校长会向一些学生发送一条短信(具体来说,在第 i 天,校长会向学号在区间 [Li,Ri] 内的学生发送一条短信,每天的 Li 和 Ri 可能不同);从第 1 天开始,一共持续 n 天。

现在,学校的教导主任想要知道,在第 Ai 天到第 Bi 天(包含第 Ai 天和第 Bi 天),哪个学生收到的短信数量最多,最多数量是多少。

由于教导主任太忙了,所以现在麻烦你帮他找到答案。

输入格式
输入文件第一行一个正整数 n,表示校长连续 n 天向学生发送短信。

接下来 n 行,每行两个正整数 Li,Ri,表示第 i 天校长向学号在区间 [Li,Ri] 内的同学发一条短信。

接下来一行一个正整数 m,表示教导主任有 m 个询问。

接下来 m 行,每行两个正整数 Ai,Bi,表示询问第 Ai 天到第 Bi 天(包含第 Ai 天和第 Bi 天),哪个学生收到的短信数量最多,最多数量是多少。

输出格式
输出文件共 m 行,每行输出空格隔开的两个正整数表示对应的教导主任询问的答案。

每行的第一个数表示收到最多短信的学生的学号,如果有多个学生均收到了最多数量的短信,你只需要输出他们之中最小的学号。

每行的第二个数表示该学生在第 Ai 天到第 Bi 天(包含第 Ai 天和第 Bi 天)一共收到了多少条短信。

数据范围
对于30%的数据,1≤n≤5×102,1≤Li≤Ri≤5×102,1≤m≤5×102
对于70%的数据,1≤n≤5×103,1≤Li≤Ri≤5×103,1≤m≤5×103
对于100%的数据,1≤n≤5×104,1≤Li≤Ri≤5×104,1≤m≤5×104,1≤Ai≤Bi≤n
输入样例:
5
1 5
1 3
3 5
2 2
4 10
3
1 1
1 3
3 4
输出样例:
1 1
3 3
2 1

新人刚学c一个多月 把自己写这个题的过程都记录在这了 (从下往上) 希望对大家能有所帮助

//差分数组 + 离线莫队 分块 奇偶优化(每块排序方向来回颠倒 减少右侧下标移动的距离)

#include <stdio.h>
#include <string.h>
#include <math.h>
#define numMAX 50000                                //本题天数 学生数
typedef struct block{
    int cal;                                        //查询顺序
    int begDate;                                    //查询起始天数
    int endDate;                                    //查询结束天数
}block;
void quicksort(block a[],int lo,int hi,int order);  //快排 order==0为增序 1为降序
int main(){
    //n为天数 m为查询次数 i j k循环 c d p q t暂存
    //stu记录学生在某些天 收到信息次数 gap为stu的差分数组 max s暂存每次查询的结果
    int n,m,i,j,k,c,d,p,q,t,stu[numMAX+1],gap[numMAX+1],max=0,s;
    memset(stu,0,sizeof(stu));                      //初始化
    memset(gap,0,sizeof(gap));

scanf("%d",&n);
int send[n+1][2];                               //记录1~n天 发送信息的学生编号区间
for(i=1;i<=n;i++){
	scanf("%d %d",&send[i][0],&send[i][1]);
	if(send[i][0]>max) max=send[i][0];          //max此处暂存一下 区间左端点的最大值
}

int numblock=sqrt(max);                         //分块数(用max来做 有时会小一些)

gap[send[1][0]]++;                              //初始化p q
if(send[1][1]<numMAX) gap[send[1][1]+1]--;
p=q=1;

scanf("%d",&m);
int rec[m+1][2];                                //记录m次查询的起始天数和结束天数
for(i=1;i<=m;i++) scanf("%d %d",&rec[i][0],&rec[i][1]);

block b[numblock][m];                           //将m次查询 分块记录
int top[numblock];                              //记录每块的大小
memset(top,0,sizeof(top));

for(i=1;i<=m;i++){
    t=rec[i][0]/numblock;                       //用左端点确定在哪块
    b[t][top[t]].begDate=rec[i][0];             //记录信息
    b[t][top[t]].endDate=rec[i][1];
    b[t][top[t]].cal=i;
    top[t]++;                                   //该块大小+1
}

//对每块 分别进行快排 并交替排序顺序 增减增减 为后续计算加快速度
for(i=0;i<numblock;i++) quicksort(b[i],0,top[i],i%2);

//    //测试 分块和排序结果是否正确
//    printf("numblock = %d\n",numblock);
//    for(i=0;i<numblock;i++){
//        for(j=0;j<top[i];j++){
//            printf("i=%d j=%d cal=%d beg=%d end=%d\n",i,j,b[i][j].cal,b[i][j].begDate,b[i][j].endDate);
//        }
//    }


int res1[m+1],res2[m+1];                        //按顺序记录要输出的结果
for(i=0;i<numblock;i++){                        //遍历b[][]
    for(j=0;j<top[i];j++){
        c=b[i][j].begDate;                      //c d暂存
        d=b[i][j].endDate;
        max=0;                                  //每轮都要重置为0
        
        //莫队 即根据上轮的结果 左右移动端点 使其成为本轮的差分数组结果
        while(p>c){
            p--;
            gap[send[p][0]]++;
            if(send[p][1]<numMAX) gap[send[p][1]+1]--;
        }
        while(p<c){
            gap[send[p][0]]--;
            if(send[p][1]<numMAX) gap[send[p][1]+1]++;
            p++;
        }
        while(q<d){
            q++;
            gap[send[q][0]]++;
            if(send[q][1]<numMAX) gap[send[q][1]+1]--;
        }
        while(q>d){
            gap[send[q][0]]--;
            if(send[q][1]<numMAX) gap[send[q][1]+1]++;
            q--;
        }
        
        //遍历差分数组 生成stu结果
        for(k=1;k<=numMAX;k++){
            stu[k]=stu[k-1]+gap[k];
            if(stu[k]>max){                     //同时得到应输出的内容
                max=stu[k];
                s=k;
            }
        }
        res1[b[i][j].cal]=s;                    //根据输出顺序 记录该输出内容
        res2[b[i][j].cal]=max;
    }
}

for(i=1;i<=m;i++) printf("%d %d\n",res1[i],res2[i]);
    
return 0;
}
void quicksort(block a[],int lo,int hi,int order){
    int n=hi-lo,i=lo,j=hi-1;
    block t;
    t.begDate=a[lo].begDate;
    t.endDate=a[lo].endDate;
    t.cal=a[lo].cal;
    if(n<=1) return;
    else{
        if(order==0){                               //根据需要 增序或降序
            while(i<j){
                while(i<j && a[j].endDate >= t.endDate) j--;
                a[i].begDate=a[j].begDate;
                a[i].endDate=a[j].endDate;
                a[i].cal=a[j].cal;
                while(i<j && a[i].endDate <= t.endDate) i++;
                a[j].begDate=a[i].begDate;
                a[j].endDate=a[i].endDate;
                a[j].cal=a[i].cal;
            }
            a[i].begDate=t.begDate;
            a[i].endDate=t.endDate;
            a[i].cal=t.cal;
            quicksort(a,lo,i,order);
            quicksort(a,i+1,hi,order);
        }
        else{
            while(i<j){
                while(i<j && a[j].endDate <= t.endDate) j--;
                    a[i].begDate=a[j].begDate;
                    a[i].endDate=a[j].endDate;
                    a[i].cal=a[j].cal;
                while(i<j && a[i].endDate >= t.endDate) i++;
                    a[j].begDate=a[i].begDate;
                    a[j].endDate=a[i].endDate;
                    a[j].cal=a[i].cal;
            }
            a[i].begDate=t.begDate;
            a[i].endDate=t.endDate;
            a[i].cal=t.cal;
            quicksort(a,lo,i,order);
            quicksort(a,i+1,hi,order);
        }
    }
}

//差分数组 + 在线莫队暴力版改进 但结果不对 需要再想

#include <stdio.h>
#include <string.h>
#define numMAX 50000
int main(){
    int n,m,a,b,i,j,k,p,q,t,stu[numMAX+1],gap[numMAX+1],max,s;
    memset(gap,0,sizeof(gap));
    scanf("%d",&n);
    int send[n+1][2];
    for(i=1;i<=n;i++) scanf("%d %d",&send[i][0],&send[i][1]);
    gap[send[1][0]]++;
    if(send[1][1]<numMAX) gap[send[1][1]+1]--;
    p=q=1;
    scanf("%d",&m);
    for(i=0;i<m;i++){
        scanf("%d %d",&a,&b);
        memset(stu,0,sizeof(stu));
        max=0;
    
    //改进 对应两种情况 即 相邻的两次查询 查询范围没有交集
    //不知为何 改完不对了
    if(q<a){
        t=q;
        q=a-1;
        a=t+1;
    }
    else if(p>b){
        t=p;
        p=b+1;
        b=t-1;
    }
    
    //在线莫队 暴力
    while(p>a){
        p--;
        gap[send[p][0]]++;
        if(send[p][1]<numMAX) gap[send[p][1]+1]--;
    }
    while(p<a){
        gap[send[p][0]]--;
        if(send[p][1]<numMAX) gap[send[p][1]+1]++;
        p++;
    }
    while(q<b){
        q++;
        gap[send[q][0]]++;
        if(send[q][1]<numMAX) gap[send[q][1]+1]--;
    }
    while(q>b){
        gap[send[q][0]]--;
        if(send[q][1]<numMAX) gap[send[q][1]+1]++;
        q--;
    }
    
    for(j=1;j<=numMAX;j++){
        stu[j]=stu[j-1]+gap[j];
        if(stu[j]>max){
            max=stu[j];
            s=j;
        }
    }
    printf("%d %d\n",s,max);
}
return 0;

}

//差分数组 + 在线莫队暴力版 因为在线 所以只能暴力

#include <stdio.h>
#include <string.h>
#define numMAX 50000
int main(){
    int n,m,a,b,i,j,k,p,q,stu[numMAX+1],gap[numMAX+1],max,s;
    memset(gap,0,sizeof(gap));
    scanf("%d",&n);
    int send[n+1][2];
    for(i=1;i<=n;i++) scanf("%d %d",&send[i][0],&send[i][1]);
    gap[send[1][0]]++;
    if(send[1][1]<numMAX) gap[send[1][1]+1]--;
    p=q=1;
    scanf("%d",&m);
    for(i=0;i<m;i++){
        scanf("%d %d",&a,&b);
        memset(stu,0,sizeof(stu));
        max=0;
        
        //暴力莫队
        while(p>a){
            p--;
            gap[send[p][0]]++;
            if(send[p][1]<numMAX) gap[send[p][1]+1]--;
        }
        while(p<a){
            gap[send[p][0]]--;
            if(send[p][1]<numMAX) gap[send[p][1]+1]++;
            p++;
        }
        while(q<b){
            q++;
            gap[send[q][0]]++;
            if(send[q][1]<numMAX) gap[send[q][1]+1]--;
        }
        while(q>b){
            gap[send[q][0]]--;
            if(send[q][1]<numMAX) gap[send[q][1]+1]++;
            q--;
        }
        
        for(j=1;j<=numMAX;j++){
            stu[j]=stu[j-1]+gap[j];
            if(stu[j]>max){
                max=stu[j];
                s=j;
            }
        }
        printf("%d %d\n",s,max);
    }
    return 0;
}

//差分数组 + 记忆化 段错误 gap占空间太大

#include <stdio.h>
#include <string.h>
#define numMAX 50000
int main(){
    int n,m,a,b,i,j,k,stu[numMAX+1],max,s;
    scanf("%d",&n);
    int gap[n+1][numMAX+1];
    memset(gap,0,sizeof(gap));
    for(i=1;i<=n;i++){
        scanf("%d %d",&a,&b);
        memcpy(gap[i],gap[i-1],numMAX+1);
        gap[i][a]++;
        if(b<numMAX) gap[i][b+1]--;
    }
    scanf("%d",&m);
    for(i=0;i<m;i++){
        scanf("%d %d",&a,&b);
        memset(stu,0,sizeof(stu));
        max=0;
        for(j=1;j<=numMAX;j++){
            stu[j]=stu[j-1]+gap[b][j]-gap[a-1][j];
            if(stu[j]>max){
                max=stu[j];
                s=j;
            }
        }
        printf("%d %d\n",s,max);
    }
    return 0;
}

//差分数组 会超时

#include <stdio.h>
#include <string.h>
#define numMAX 50000
int main(){
    int n,m,a,b,i,j,k,stu[numMAX+1],gap[numMAX+1],max,s;
    scanf("%d",&n);
    int send[n+1][2];
    for(i=1;i<=n;i++) scanf("%d %d",&send[i][0],&send[i][1]);
    scanf("%d",&m);
    for(i=0;i<m;i++){
        scanf("%d %d",&a,&b);
        memset(gap,0,sizeof(gap));
        memset(stu,0,sizeof(stu));
        max=0;
        for(j=a;j<=b;j++){
            gap[send[j][0]]++;
            if(send[j][1]<numMAX) gap[send[j][1]+1]--;
        }
        for(j=1;j<=numMAX;j++){
            stu[j]=stu[j-1]+gap[j];
            if(stu[j]>max){
                max=stu[j];
                s=j;
            }
        }
        printf("%d %d\n",s,max);
    }
    return 0;
}

//暴力解法 会超时

#include <stdio.h>
#include <string.h>
#define numMAX 50000
int main(){
    int n,m,a,b,i,j,k,stu[numMAX+1],max,s;
    scanf("%d",&n);
    int send[n+1][2];
    for(i=1;i<=n;i++) scanf("%d %d",&send[i][0],&send[i][1]);
    scanf("%d",&m);
    // printf("\n");
    for(i=0;i<m;i++){
        scanf("%d %d",&a,&b);
        memset(stu,0,sizeof(stu));
        max=0;
        for(j=a;j<=b;j++){
            for(k=send[j][0];k<=send[j][1];k++) stu[k]++;
        }
        for(j=1;j<=numMAX;j++){
            if(stu[j]>max){
                max=stu[j];
                s=j;
            }
        }
        printf("%d %d\n",s,max);
    }
    return 0;
}
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页