HDU 5726 2016多校Contest 1 D题【好玩题,注意PE,和map初始化】

因为case输出后要换行,被PE了一发……

因为map多组数据没初始化,又WA了一发……哎


题目大意


给定n个数字, 问[L,R]区间的所有数字的gcd是多少。如果是K的话, 有多少个区间的gcd也是K。


做法:计算区间GCD的问题,求K的话,显然是线段树啊,ST算法都能解决。这里不再赘述。


然后就是根据题解的思想~~ 


g[i][] 表示第i(从1开始计数,不是从0开始)个数字为区间起点,随着区间的终点往右推进,可以得到哪些gcd的值。

举个例子,数列4 9 6 6 3 2

以9为起点,那么

g[2][]里保存的就是9,3,3,3,1. 因为分别为【9】 【9 6】【9 6 6】【9 6 6 3】【9 6 6 3 2】

因为【显然】,这个g数组里的元素值是单调递减的~  所以我们不需要记录那么多3,只要记录有多少个6就可以了。


比如还是上面的例子,g[2][0] = 一个9     g[2][1] = 3个3,  g[2][2] = 一个1。  也就是说,保存的数字数量是log级的。


对于求g[1][]的值的时候,很方便,可以直接利用g[2]里保存的元素直接求出。 因为gcd求解满足结合律。。相当于先求后面的,再和4(数列的第一个元素)求一下gcd。


这样求解就很快,也是log级别的。


最后我们在求解的过程中已经保存了诸如“一个9, 3个3”这样的数据,都存起来,到时候问问题的时候,直接查就行了。(我用的是map)



 

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


typedef long long LL;
const int maxn = 100020;
const int maxnode = maxn * 4;
int n, m;
LL a[maxn];
vector<LL>g[maxn];
vector<LL>w[maxn];
map<LL, LL>mp;

LL gcd(LL a, LL b)
{
    return a%b?gcd(b,a%b):b;
}


void init()
{
	scanf("%d", &n);
	for (int i = 0; i <= n; ++ i)
	{
		g[i].clear();
		w[i].clear();
	}
	mp.clear();
	for (int i = 1; i <= n; ++ i)
		scanf("%lld", &a[i]);

}

inline void ins(LL t, LL p)
{
	if (mp.find(t) == mp.end())	mp[t] = p;
	else mp[t] += p;
}


LL gd[maxnode];

void build(int o, int L, int R)
{
	if (L == R)
	{
		gd[o] = a[L];
		return ;
	}
	int M = L + (R- L) / 2;
	int lc = o * 2, rc = o * 2 + 1;
	build(lc, L, M);
	build(rc, M + 1, R);
	gd[o] = gcd(gd[lc], gd[rc]);
}

int ql, qr;
LL ans;
void query(int o, int L, int R)
{
	if (ql <= L && R <= qr)
	{
		ans = gcd(ans, gd[o]);
		return;
	}
	int M = L + (R -L) / 2;
	int lc = o * 2, rc = o * 2 + 1;
	if (ql <= M)	query(lc, L, M);
	if (qr > M)	query(rc, M + 1, R);
}


void doit()
{
	g[n].push_back(a[n]);
	w[n].push_back(1);
	ins(a[n], 1);
	for (int i = n - 1; i >= 1; -- i)
	{
		g[i].push_back(a[i]);
		w[i].push_back(1);
		ins(a[i], 1);
		LL tmp = a[i];
		for (int j = 0; j != g[i + 1].size(); ++ j)
		{
			LL t = g[i + 1][j];
			LL num = w[i + 1][j];
			t = gcd(tmp, t);
			if (t != tmp)
			{
				tmp = t;
				g[i].push_back(tmp);
				w[i].push_back(num);
			}else
			{
				w[i][w[i].size() - 1] += num;
			}
			ins(tmp, num);
		}
	}
	scanf("%d", &m);
	build(1, 1, n);
	while (m--)
	{
		scanf("%d%d", &ql, &qr);
		ans = a[ql];
		query(1, 1, n);
		printf("%lld %lld\n", ans, mp[ans]);
	}
}

int main()
{
	int T;
	scanf("%d", &T);
	for (int i = 1; i <= T; ++i)
	{
		printf("Case #%d:\n", i);
		init();
		doit();
	}

	return 0;
}
/*
4
3 3
##G
G##
###
1.7959

*/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值