HDU - 5875 Function [单调性剪枝+预处理]

2016大连网络赛 H题 传送门:HDU - 5875


Function

Time Limit: 7000/3500 MS (Java/Others)
Memory Limit: 262144/262144 K (Java/Others)

Problem Description

  The shorter, the simpler. With this problem, you should be convinced of this truth.

  You are given an array A of N postive integers, and M queries in the form \((l,r)\). A function \(F(l,r) (1≤l≤r≤N)\) is defined as:

\[ F(l,r)=\begin{cases} A_l & l=r \\ F(l,r-1) mod A_r & l<r \\ \end{cases}\]

  You job is to calculate F(l,r), for each query (l,r).

Input

  There are multiple test cases.

  The first line of input contains a integer T, indicating number of test cases, and T test cases follow.

  For each test case, the first line contains an integer \(N(1≤N≤100000)\).
  The second line contains N space-separated positive integers: \(A_1,…,A_N (0≤A_i≤10_9)\).
  The third line contains an integer M denoting the number of queries.
  The following M lines each contain two integers \(l,r (1≤l≤r≤N)\), representing a query.

Output

  For each query\((l,r)\), output \(F(l,r)\) on one line.

Sample Input

1
3
2 3 3
1
1 3

Sample Output

2

Source

2016 ACM/ICPC Asia Regional Dalian Online


题目大意:

  • 给定序列\(A_N\),操作\(F(l,r)\)表示查询\(A_l mod A_{l+1} mod……mod A_{r}\)

基本思路:

  • 首先要分析多次取模操作的特征。\(a mod b\)对于a<b时无意义,\(a mod b mod c\)对于b<c无意义。所以,区间里有效取模应该是递减的。
  • 再进一步思考可以发现 取模过程的递减非常快 ,至少也是\(a mod b > a/2\),注意到一旦a变成0就可以不考虑后面的区间,也就是说施加取模次数是log级的,最多也就30次左右。

具体实现:

  • 预处理是个很大的问题,最简单的办法是 记录\(A_i\)后面第一个比\(A_i\)小的数的位置\(n^2\)复杂度明显太慢。但仔细一想,只有非常极端的情况复杂度才会达到\(n^2\)。(然后居然真的就这么过了哈哈哈)
  • 考虑更优化的预处理方法:每次只需要知道这个数后面第一个比它小的数,有点类似区间最小值,只不过要从左到右选择区间,在RMQ上进行修改即可。
  • 查询过程就很容易了实现了,每次跳到下一个位置,当前值为0或者超出区间则退出。

注意事项:

  • 根据题意(和做题习惯),查询操作应该是很多,所以一定要预处理。每次查询都找一边当前区间的LIS的做法是不行的。
  • 所以这不就是个区间LIS么。

Codes:

暴力版本:

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 100010;

int main()
{
    int t,n,m,a[MAXN],nex[MAXN];
    scanf("%d",&t);

    while(t--)
    {
    	scanf("%d",&n);
    	for(int i=1; i<=n; ++i)
    		scanf("%d",&a[i]);

    	for(int i=1; i<=n; ++i)
    	{
    		nex[i] = 0;
    		for(int j=i+1; j<=n; ++j)
    			if(a[i] > a[j])
    			{
    				nex[i] = j;
    				break;
    			}
    	}
    	
    	scanf("%d",&m);
    	while(m--)
    	{
    		int l,r;
    		scanf("%d %d",&l,&r);
    		int ans = a[l],now = nex[l];
    		while(now <= r && now != 0)
    		{
    			ans %= a[now];
    			if(ans == 0)
    				break;
    			now = nex[now];
    		}
    		printf("%d\n",ans);
    	}
    }

    return 0;
}

修改RMQ版本:


咕咕咕

posted @ 2018-08-30 17:48  摸鱼鱼  阅读(226)  评论(0编辑  收藏  举报