UESTC 1256 昊昊喜欢运动 n^2的预处理 or 前缀和

117 篇文章 2 订阅
53 篇文章 0 订阅

           

昊昊爱运动

Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others)
 

昊昊喜欢运动

N 天内会参加 M 种运动(每种运动用一个 [1,m]   的整数表示)

舍友有 Q 个问题

问昊昊第 l 天到第 r 天参加了多少种不同的运动

Input

输入两个数 N M  ( 1N2000 1M100 );

输入 N 个数 ai 表示在第i天昊昊做了第 ai 类型的运动;

输入一个数 Q ( 1Q106 );

输入 Q 行 每行两个数  r ( 1lrn      );

Output

一共 Q

每一行输出一个数 表示昊昊在第 l 天到第 r 天一共做了多少种活动

Sample input and output

Sample Input Sample Output
5 3
1 2 3 2 2
3
1 4
2 4
1 5
3
2
3

Source

第七届ACM趣味程序设计竞赛第二场(正式赛) A

My Solution

1、当时把时间改成3000MS
所以直接暴力,过去,用一个数字cot[108],来记录每个区间内的任务是否在区间类出现过。
现在时间突然该回来了,所以 嘿嘿,我只是记录一下自己的代码☺☺ ☺☺ ☺☺

#include <iostream>
#include <cstdio>
#include <cstring>
#define LOCAL
using namespace std;
int n[2008],cot[108];
int main()
{
    #ifdef LOCAL
    freopen("a.txt","r",stdin);
    #endif // LOCAL
    int N,M,Q,x,y,cott;
    scanf("%d%d",&N,&M);
    for(int i=1;i<=N;i++){
        scanf("%d",&n[i]);
    }

    scanf("%d",&Q);
    while(Q--){
        cott=0;
        memset(cot,0,sizeof(cot));
        scanf("%d%d",&x,&y);
        for(int i=x;i<=y;i++)
            if(cot[n[i]]==0) {cott++;cot[n[i]]++;}
        //for(int j=0;j<=M;j++)
        //    if(cot[j]!=0) ;
        printf("%d",cott);
        if(Q) printf("\n");

    }
    return 0;
}

2、用n^2的预处理,直接搞出ans[i][j],然后O(1)的询问
当时2000的平方算成1e8级了,☺☺,事实上是1e6级。

#include <cstdio>
#include <cstring>
//#define LOCAL
using namespace std;
const int maxn=2000+2;
int n[maxn],cot[108],ans[maxn][maxn];
int main()
{
    #ifdef LOCAL
    freopen("a.txt","r",stdin);
    #endif // LOCAL
    int N,M,Q,x,y,cott;
    scanf("%d%d",&N,&M);
    for(int i=1;i<=N;i++){
        scanf("%d",&n[i]);
    }
    //ans[maxn][maxn]就不用memset了,由于这里没有必要,而且数组比较大,memset起来还是要耗点时间的
    //n^2的预处理
    for(int i=1;i<=N;i++){
        memset(cot,0,sizeof(cot));cott=0;
        for(int j=i;j<=N;j++){
            if(cot[n[j]]==0) {cott++;cot[n[j]]++;}
            ans[i][j]=cott;//由于这里指的是[x,y],且1<=x,所以上面要用 int i=1;i<=N;i++,下面j也是
        }   //数组和所需要对应好
    }
    //询问
    scanf("%d",&Q);
    while(Q--){
        scanf("%d%d",&x,&y);
        printf("%d",ans[x][y]);
        if(Q) printf("\n");
    }
    return 0;
}


3、前缀和思想
在输入的时候直接维护ans[ i ][ j ],表示在[1,i ]中j 出现了多少次,维护的时候注意把前一个的所有信息都要维护的这一项;
然后每次询问扫M,判断ans[ y ][ i ]-ans[ x-1 ][ i ]是否大于0 。用cott计数;

#include <cstdio>
#include <cstring>
#define LOCAL
using namespace std;
int ans[2002][108];
int main()
{
    #ifdef LOCAL
    freopen("a.txt","r",stdin);
    #endif // LOCAL
    int N,M,Q,x,y,cott,n;
    scanf("%d%d",&N,&M);
    memset(ans,0,sizeof(ans));
    for(int i=1;i<=N;i++){
        scanf("%d",&n);                                //原来的n[maxn]也可以省掉了
        ans[i][n]++;
        for(int j=1;j<=M;j++) ans[i][j]+=ans[i-1][j];  //边输入边维护 前面的所有信息都要维护过来1-M的出现次数
        //前面用了j<i,i可能很大,不越界才怪,改的时候,不要漏地方没改
    }

    scanf("%d",&Q);
    while(Q--){
        cott=0;
        scanf("%d%d",&x,&y);
        for(int i=1;i<=M;i++){
            if((ans[y][i]-ans[x-1][i])>0) cott++;    //注意索引
        }
        printf("%d",cott);
        if(Q) printf("\n");
    }
    return 0;
}
前面数组越界了,返回了一个RF,在不知道是越界引起的时候,我加了个#include <iostream> 以后返回了WA,嘿嘿挺好玩的。
用前缀和相对n^2预处理memory小了很多,但显然n^2预处理要快一点,
特别是Q比较大,很接近上限的时候,Q->1e6,且n->2000,m->100,O(n*m+Q*m)>>O(n^2+Q)。

谢谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值