UVALive 6582 Magical GCD

题意:

有一个n个数的序列, 定义magic gcd为一个连续子序列的gcd乘上子序列长度, 求magic gcd的最大值。

思路:

对于以i为右端点的子序列,左端点从i到1,gcd肯定是越来越小的, 并且gcd是分段的,也就是说,对于一段连续的左端点[a,b], gcd(a,a+1,... i) = gcd(b, b + 1,... i), 由于gcd减小一次最少除以2,所以对于每个右端点,gcd最多有log10^12段, 而对于每段,取左端点和gcd就行了。而对于每个右端点的分段可以递推算出。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <stack>
#include <cmath>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <ctime>
#include <cstdlib>
using namespace std;

#define mxn 100020
#define mxe 200020
#define mod 10000007
#define LL long long
#define inf 0x3f3f3f3f
#define vi vector<int>
#define PB push_back
#define MP make_pair
#define pii pair<int, int>
#define G(i, u) for(int i = fst[u]; ~i; i = nxt[i])
#define F(i, n) for(int i = 1; i <= n; ++i)
#pragma comment(linker,"/STACK:1024000000,1024000000")
#define ls (i << 1)
#define rs (ls | 1)
#define md ((ll + rr) >> 1)

int n;
LL a[mxn];
vector<pair<int, LL> > g[mxn];

void init() {
	for(int i = 0; i < mxn; ++i)
		g[i].clear();
}
LL gcd(LL x, LL y) {
	if(y == 0) return x;
	return gcd(y, x % y);
}
int main() {
//	freopen("tt.txt", "r", stdin);
	int cas;
	scanf("%d", &cas);
	while(cas--) {
		init();
		scanf("%d", &n);
		for(int i = 1; i <= n; ++i)
			scanf("%lld", &a[i]);
		g[1].PB(MP(0, a[1]));
		for(int i = 2; i <= n; ++i) {
			LL G = gcd(a[i], a[i-1]);
			if(G != a[i])
				g[i].PB(MP(i - 1, a[i]));
			int len = g[i-1].size();
			for(int j = 0; j < len; ++j) {
				int p = g[i-1][j].first;
				LL val = g[i-1][j].second;
				if(p == 0) {
					g[i].PB(MP(0, G));
					continue;
				}
				LL tmp = gcd(G, a[p]);
				if(G == tmp) continue;
				g[i].PB(MP(p, G));
				G = tmp;
			}
		}
		LL ans = n;
		for(int i = 1; i <= n; ++i) {
			int len = g[i].size();
			for(int j = 0; j < len; ++j) {

				int p = g[i][j].first;
				LL val = g[i][j].second;
				ans = max(ans, (i - p) * val);
			}
		}
		printf("%lld\n", ans);
	}
	return 0;
}


你可能感兴趣的:(UVALive 6582 Magical GCD)