中位数的和_KEY

中位数的和

(number.pas/c/cpp)

【题目描述】

flower 有 N-1 个朋友,他们要一起玩一个游戏:首先确定三个非负整数 a,b,c,然后每个人依次在纸上写一个数,设第 i 个人写下的数字为 f[i],flower 先写下数字 f[1]=1,对于第 i 个写数字的人(i>1)有: f[i]=(a*m[i-1]+b*i+c)mod1,000,000,007;其中 m[i-1]为前 i-1 个写下的数字的中位数,如果 i-1 为偶数,那么取靠前的那个数。flower 想要知道,所有人写下的数字的和。

【输入格式】

输入仅一行,包含四个非负整数 a,b,c,N;意义如上;

【输出格式】

输出只有一行一个整数,表示数字和。

【输入样例】

3 1 2 6

【输出样例】

103

【数据规模】

对于 30%的数据: N≤1,000;

对于 100%的数据: N≤200,000; a,b,c≤1,000,000,007。

 

第一次拿到这道题,给我的感觉是F[1~i-1]一定比F[i]小,所以中位数一定是F[i>>1](CJJDs也是这样想的),但马上被我否认了,因为我注意到有一个mod,所以值可能会变小。于是立马换一种思路,既然叫我们求中位数,即中间的数,那么我们将已存的有序数列掰成两半,中间的即为中位数。那么这里就需要用到一个大根堆,用于存储前半段数,一个小根堆,用于存储后半段数。每次将生成的数放入小根堆,如果当前是第i次操作且i为奇数,那么将小根堆顶放入大根堆。注意mod后如果小于大根堆顶就交换。

code

#include <cstdio>
#include <cctype>
#include <queue>
#define mod 1000000007
#define C c = tc ( )
using namespace std;

inline char tc(){
    static char fl[100000],*A,*B;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}

inline void read(long long &x){
    static char c;
    while(!isdigit(C));x=c-'0';
    while(isdigit(C))x=x*10+c-'0';
}

long long a,b,c,n;
long long f[200001];
priority_queue<long long>w1;
priority_queue<long long,vector<long long>,greater<long long> >w2;

int main(){

    freopen("number.in","r",stdin);
    freopen("number.out","w",stdout);

    read(a),read(b),read(c),read(n);
    w1.push(f[1]=1);
        for(int i=2;i<=n;i++){
            long long val=w1.top(),ve;
            f[i]=(val*a+b*i+c)%mod;ve=f[i];
            if(ve<val)w1.pop(),w1.push(ve),ve=val;
            w2.push(ve);
            if(i&1)val=w2.top(),w2.pop(),w1.push(val);
        }
    long long ans=0;
        for(int i=1;i<=n;i++)ans+=f[i];
    printf("%lld",ans);

    fclose(stdin),fclose(stdout);
    return 0;
}

最后引用CJJ的话。

如果是Pascal的话我也可以,用Pascal打堆!

转载于:https://www.cnblogs.com/Cptraser/p/7593452.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值