【题目描述】
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; }