HDU 3706——Second My Problem First

【题目描述】

Give you three integers n, A and B. 
Then we define Si = Ai mod B and Ti = Min{ Sk | i-A <= k <= i, k >= 1}
Your task is to calculate the product of Ti (1 <= i <= n) mod B.

【输入】

Each line will contain three integers n(1 <= n <= 107),A and B(1 <= A, B <= 231-1). 
Process to end of file.

【输出】

For each case, output the answer in a single line.

【输入样例】

1 2 3

2 3 4

3 4 5

4 5 6

5 6 7

【输出样例】

2

3

4

5

6

【作者】

WhereIsHeroFrom@HDU

【来源】

HDOJ 5th Anniversary Contest

【算法分析】

计算 Si ,然后取出 [ i - A  , i ] 中,最小的 Si ,累乘到 Ti 中,最后输出 Ti mod B 的值。

计算 Si ,我们可以递归的计算,由于但是怎么找 [ i - A , i ] 中最小的 Si 呢?

难道直接枚举?

不!绝对TLE, 10^7 的数据规模

那么怎么优化?

首先明确一点:我们必须要有一个 10^7 的循环。为什么?因为 我们要累乘计算 Ti

那么,优化的地点明确了,就是优化怎么找 [ i - A , i ] 中,最小的 Si

我们观察一下这个查找的区间: [ i - A , i ] 有没有发现什么?

特点:随着 i 的增加,这个区间就像一个移动的窗口一样向右移动了一个单位的长度!!

那么,也就是说,每次移动窗口的时候,窗口左边的元素要移出去,窗口右边的元素要新加一个进来,想到了什么数据结构?

队列!!

但是光是这样就够了吗?我们怎么根据这个特点来找最小的元素?

我们可以保持队列中元素的单调性!!!

什么叫保持队列中的单调性?

就是说,队列中的元素,是单调递增或者是单调递减的!!但是这道题要求队列中的元素是单调递增的,也就是说,队首的元素是整个队列中最小的元素,队首元素的 id 也是整个队列中最小的

别那么,问题来了,怎么实现把队列中的元素变成单调递增的?

普通队列是一个端口进,一个端口出,那么,为什么我们不能两个端口出,一个端口进呢??

每次向队列中添加元素的时候,检查这个元素前面的元素是否比他大,如果比他大,加进去是不是就破坏了整个单调性了?

这好办啊,我们直接把他前面的元素删除了不就完了,如果删除了之后还是这种情况,那么继续删除,直到他前面的元素小于他为止。

这里又有一个问题了,我们删除这个元素是否会影响我们求 [ i - A , i ] 这个区间的最小值?

想想为什么将这个元素删除了就知道了,答案是否定的

这就是所谓的单调队列,也叫双端队列

下面给出这道题的实现代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define INF 0x7fffffff
using namespace std;
int main()
{
    int n,A,B;
    int q[100001],id[100001];
    while(scanf("%d%d%d",&n,&A,&B)!=EOF)
    {
        __int64 Ti=1,s=1;
        int head=0,tail=0;
        for(int i=1;i<=n;i++)
        {
            s=(s*A)%B;
            while(head<tail && q[tail]>=s) tail--;
            q[++tail]=s;
            id[tail]=i;
            while(id[head+1]<i-A) head++;
            Ti=(Ti*q[head+1])%B;
        }
        printf("%I64d\n",Ti);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值