解题报告 之 HDU5288 OO' s Sequence

解题报告 之 HDU5288 OO‘ s Sequence

Description

OO has got a array A of size n ,defined a function f(l,r) represent the number of i (l<=i<=r) , that there‘s no j(l<=j<=r,j<>i) satisfy a imod a j=0,now OO want to know

Input

There are multiple test cases. Please process till EOF.

In each test case:

First line: an integer n(n<=10^5) indicating the size of array

Second line:contain n numbers a i(0<a i<=10000)

Output

For each tests: ouput a line contain a number ans.

Sample Input

5
1 2 3 4 5 

Sample Output

23 

题目大意:给你n个数的序列(当中数可能反复)。对于每个子区间,求该子区间中没有因子也在该子区间的的个数。再把全部子区间内这种数的数量求和。比方例子中的 1
2 3 4 5,那么子区间[1,2,3]中这种数就是1。2,3三个。然后对于子区间2 3 4,这种数就仅仅有两个,由于4有因子2也在该子区间中。

分析:非常自然的想法是遍历每个子区间,再统计有多少个数,再加起来。但这样做是不可行的。这样就中了题目的陷阱,被那个公式给误导了,所以我们必需要跳出惯性思维。将关注的单位从子区间变到每个数。

考虑一个数。它能被统计多少次取决于什么呢?取决于它在多少个子区间内可以做到没有因子。所以我们非常自然的去关注离他近期的左右两个因子。由于这两个因子范围以外的子区间都没有卵用。

。比方5
5 2 3 3 4 3 2 5 5。那么对于4来说,我们找到左右两个因子2之后,就能够发现从5開始和结束的子区间都不会算到4。由于有2在那里杵着。

至此,问题转化为,找到每个数左右离它近期的因子。然后就行非常easy的知道这个数可以被统计多少次了。那么怎么去寻找左右两边的因子呢?有两种做法,首先介绍我的方法。注意到可能的数字一共仅仅有1e4个。先从左到右扫描依次更新两个数据,一是这个数最后出现的位置,用loc数组表示,还有一个是这个数左边离它近期的因子的位置则用该数的每个因子(遍历),求全部因子中最后出现的最大值。

然后再从右到左扫描,原理一样的。完毕之后再遍历序列,对于每个数求它被统计多少次就可以。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long ll;
const int MAXN = 1e5 + 10;
const int MAXM = 1e4 + 10;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;

int nums[MAXN]; //序列中的数
int lb[MAXN], rb[MAXN]; //序列中的数左右离他近期的因子的位置
int latest[MAXM];//某个数字最后出现的位置

int main()
{
	int n;
	while(scanf( "%d", &n ) == 1)
	{
		memset( lb, 0, sizeof lb );
		memset( rb, INF, sizeof rb );
		//reset

		for(int i = 1; i <= n; i++)
		{
			scanf( "%d", &nums[i] );
		}//input

		for(int i = 0; i < MAXM; i++)	latest[i] = 0;

		for(int i = 1; i <= n; i++)
		{
			for(int j = 1; j <= sqrt( nums[i] ); j++)
			{//遍历每一个因子
				if(nums[i] % j == 0)
				{
					lb[i] = max( lb[i], latest[j] );
					lb[i] = max( lb[i], latest[nums[i] / j] );
				}
			}
			latest[nums[i]] = i; //更新位置。注意要遍历后更新,由于本身也是自己的因子

		}// tackle 1

		for(int i = 0; i < MAXM; i++)	latest[i] = n + 1;
		for(int i = n; i >= 1; i--)
		{
			for(int j = 1; j <= sqrt( nums[i] ); j++)
			{
				if(nums[i] % j == 0)
				{
					rb[i] = min( rb[i], latest[j] );
					rb[i] = min( rb[i], latest[nums[i] / j] );
				}
			}
			latest[nums[i]] = i;
		}// tackle 2 同理

		ll ans = 0;
		for(int i = 1; i <= n; i++)
		{
			ans = (ans + (i - lb[i])*(rb[i] - i)) % MOD;
			//统计序列中每一个数被统计的次数。能够理解为范围内左边选一个数的选法*右边选一个数的选法。
		}
		printf( "%lld\n", ans );
	}
	return 0;
}

另一种方法是,记录每一个数字出现的位置,每次更新的时候用二分去找距离它近期的因子的位置。可是非常麻烦也更慢。

解题报告 之 HDU5288 OO' s Sequence

时间: 2024-10-06 12:17:27

解题报告 之 HDU5288 OO&#39; s Sequence的相关文章

解题报告 之 HDU5288 OO&#39; s Sequence

解题报告 之 HDU5288 OO' s Sequence Description OO has got a array A of size n ,defined a function f(l,r) represent the number of i (l<=i<=r) , that there's no j(l<=j<=r,j<>i) satisfy a imod a j=0,now OO want to know () Input There are multipl

hdu 1711 Number Sequence 解题报告

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1711 题目意思:给出一条有n个数的序列a[1],a[2],......,a[n],和一条有m 个数的序列b[1],b[2],......,b[m],求出b[1],b[2],...,b[m]在序列a中完全匹配时,在序列a中的位置,如果找不到输出-1. 这几天一直在学kmp,该题算是kmp的入门题吧.有个地方要稍稍注意,代码中,主串和模式串的比较初始值为-1,-1,否则如果从0开始,会默认第一个字符是相

USACO Section2.1 Sorting a Three-Valued Sequence 解题报告

sort3解题报告 —— icedream61 博客园(转载请注明出处)------------------------------------------------------------------------------------------------------------------------------------------------[题目] 给你N,而后给出N个数,每个数都是1~3中的一个.请问,要把这个数列升序排好,最少需要进行几次两两交换?[数据范围] 1<=N<

Fixed Point 解题报告

题目总结: 这种数论动规的关键点是在“与上届相等的数的处理”上,只要这个弄懂了,这种题应该就都会做了. 因为和上届相等的数最多只有一个,所以我用一个equal来记录是否有满足条件的上届.而其他小于上届的数用f数组储存. 策略只有取1和取0. 小于上届的数可以随便取. equal的状态转移要好好想想: 当前位为1:取1则equal保留,取0则equal可以转移到f数组. 当前位为0:取0则equal保留.取1就比上届大了,舍去. 一般性总结: 1.打草稿分析样例很有效果. 2.直接用不加证明的结论

poj 1094 Sorting It All Out 解题报告

题目链接:http://poj.org/problem?id=1094 题目意思:给出 n 个待排序的字母 和 m 种关系,问需要读到第 几 行可以确定这些字母的排列顺序或者有矛盾的地方,又或者虽然具体的字母顺序不能确定但至少不矛盾.这些关系均是这样的一种形式: 字母1 < 字母2 这道题目属于图论上的拓扑排序,由于要知道读入第几行可以确定具体的顺序,所以每次读入都需要进行拓扑排序来检验,这时每个点的入度就需要存储起来,所以就有了代码中memcpy 的使用了. 拓扑排序的思路很容易理解,但写起来

CodeForces 250B Restoring IPv6 解题报告

Description An IPv6-address is a 128-bit number. For convenience, this number is recorded in blocks of 16 bits in hexadecimal record, the blocks are separated by colons — 8 blocks in total, each block has four hexadecimal digits. Here is an example o

ACM-ICPC 2017 Asia HongKong 解题报告

ACM-ICPC 2017 Asia HongKong 解题报告 任意门:https://nanti.jisuanke.com/?kw=ACM-ICPC%202017%20Asia%20HongKong 按AC次序: D - Card collection In an online game, a player can collect different types of power cards. Each power card can enable a player to have a uni

Winter-2-STL-E Andy&#39;s First Dictionary 解题报告及测试数据

use stringstream Time Limit:3000MS     Memory Limit:0KB Description Andy, 8, has a dream - he wants to produce his very own dictionary. This is not an easy task for him, as the number of words that he knows is, well, not quite enough. Instead of thin

CH Round #56 - 国庆节欢乐赛解题报告

最近CH上的比赛很多,在此会全部写出解题报告,与大家交流一下解题方法与技巧. T1 魔幻森林 描述 Cortana来到了一片魔幻森林,这片森林可以被视作一个N*M的矩阵,矩阵中的每个位置上都长着一棵树,其中一些树上结有能够产生能量的魔力水果.已知每个水果的位置(Xi,Yi)以及它能提供的能量Ci.然而,魔幻森林在某些时候会发生变化:(1) 有两行树交换了位置.(2) 有两列树交换了位置.当然,树上结有的水果也跟随着树一起移动.不过,只有当两行(列)包含的魔力水果数都大于0,或者两行(列)都没有魔