GalaxyOJ-945 (逆向思维+去重优化)

题目

Problem 945: 数列的秘密
Time Limit: 1000 ms Memory Limit: 262144 KB

Problem Description

WSW最近在研究数列,他需要知道在他所研究的数列中,最大的数是多少(Max),最小的数是多少(Min),最大的数的最小的数次幂是多少(Max^Min),所有数的乘积是多少。
要知道,这样的问题是肯定难不倒WSW的。但是,最近WSW突发奇想,想要研究下这个数列的更深层的性质,所以他决定不断的从这个数列中删去一些数,每次删除后都研究下当前数列。由于数列项数很大,这给WSW带来了很大的麻烦,于是WSW请你帮他写一个程序,来完成下列操作。
1. D x:表示从数列中删除x,保证原数列中一定会有x。(注意这里是如果数列中有多个x,那么就要先把x全部删掉,如果某个x在D的时候以前已经被D过了,请自动忽略这一次的D)
2. B:输出当前数列中的最大数,保证数列不为空。
3. S:输出当前数列中的最小数,保证数列不为空。
4. M:输出Max^Min 除以317847191 的余数,其中Max 为当前数列中的最大数,Min 为当前数列中的最小数,保证数列不为空。
5. T:输出数列中所有数的乘积除以317847191 的余数,保证数列不为空。

Input

共M+2 行
第1 行:两个正整数N,M,N 表示初始数列的长度,M 表示操作数。
第2 行:N 个正整数,第i 个数表示初始数列中的第i 项Ai,数列中有可能会有相同的数。
第3~M+2 行:每行表示一个操作,具体格式参见题目描述。

对于所有数据N<=1,000,000, M<=1,000,000, Ai<=100,000,000

Output

每行一个数,分别表示每个操作的结果(D x 操作不需要有输出)。

Sample Input

3 6
2 6 9
M
D 9
B
S
M
T

Sample Output

81
6
2
36
12

分析

  • 第一想到线段树,不过发现空间会炸……
  • 后来发现都是对于整个数组的操作,可以想想一次扫过,由于模数不是质数,可以想到倒着做。
  • 再去个重才不超时……

程序

#include <cstdio>
#include <algorithm>
#define N 1000005
#define Ha 317847191
#define X q[i].x
typedef long long ll;
using namespace std;
struct que{char Q;ll x,y;} q[N];
ll i,n,m,k,a[N],S[N],F[N],num,Ans=1,Min=2147483647,Max=-2147483647;
char s[10];

ll ksm(ll xx,ll yy){
    long long x=xx,y=yy,ret=1;
    for (; y; y>>=1,x=(x*x)%Ha)
        if (y&1) ret=(ret*x)%Ha;
    return (ll)ret;
}

int main(){
    scanf("%lld%lld",&n,&m);
    for (i=1; i<=n; i++) scanf("%lld",&a[i]);
    sort(a+1,a+n+1);
    for (i=1; i<=n; i++) if (a[i]!=a[i-1]) S[++num]++; else S[num]++;
    unique(a+1,a+n+1);

    for (i=1; i<=m; i++){
        q[i].Q=(scanf("%s",s),s[0]);
        if (s[0]=='D'){
            scanf("%lld",&X);
            k=lower_bound(a+1,a+num+1,X)-a;
            if (!F[k]) F[k]=i;
        }
    }

    for (i=1; i<=num; i++) if (!F[i]) Ans=(Ans*ksm(a[i],S[i]))%Ha,Min=min(Min,a[i]),Max=max(Max,a[i]);

    for (i=m; i; i--){
        if (q[i].Q=='D'){   //添加 x 
            k=lower_bound(a+1,a+num+1,X)-a;
            if (F[k]!=i) continue;
            Ans=(Ans*ksm(q[i].x,S[k]))%Ha;
            Min=min(Min,X);
            Max=max(Max,X);
        }
        if (q[i].Q=='B'){   //输出最大值 
            q[i].y=Max;
        }
        if (q[i].Q=='S'){   //输出最小值 
            q[i].y=Min;
        }
        if (q[i].Q=='M'){   //Max^Min
            k=ksm(Max,Min);
            q[i].y=k;
        }
        if (q[i].Q=='T'){   //输出乘积 
            q[i].y=Ans;
        }
    }

    for (ll i=1; i<=m; i++) if (q[i].Q!='D') printf("%lld\n",q[i].y);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值