【HDU5930 2016CCPC东北地区大学生程序设计竞赛 - 重现赛 I】【线段树 预处理加变更贡献】GCD 动态修改维护全局gcd数量

GCD

Time Limit: 9000/4500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 78    Accepted Submission(s): 11


Problem Description
Mr. Frog likes generating numbers! He can generate many numbers from a sequence.

For a given sequence   a1,a2,,an  Mr. Frog can choose two numbers l and r ( 1lrn) and calculate the gcd between l-th and r-th number in this sequence   g=gcd(al,al+1,,ar). Asan expert in generating numbers, Mr. Frog wants to know how many distinct numbers can be generated by a sequence.

Mr. Frog likes challenges, so there may be many modifications in this sequence. In the i-th modification, Mr. Frog may change   ap  to   vi. After each modification, you are asked to tell how many distinct numbers can be generated by this sequence immediately!
 

Input
The first line contains only one integer T, which indicates the number of test cases.

For each test case, the first line includes two numbers n, q( 1n,q50000). which indicate the length of sequence and the number of modifications.

The second line contains n numbers: a1,a2,,an.

Then q lines, each line contain two numbers,   pi,vi(1pin,1vi1000000).

Test data guarantee that   1<ai1000000  all the time and the sum of all n and q is less than or equal to   2×105.
 

Output
For each test case, first output one line "Case #x:", where x is the case number (starting from 1). Then q lines, each line contain only one number, which is the answer to current sequence.
 

Sample Input
 
   
2 3 2 1 2 3 1 3 2 3 3 2 3 3 3 1 1 2 2
 

Sample Output
 
   
Case #1: 3 1 Case #2: 2 3
Hint
For case 1, after the first operation, 3,2,1 can be generated by the sequence 3, 2, 3. Whereas after the second operation, sequence 3, 3, 3 can generate only 3.
 

Source
2016CCPC东北地区大学生程序设计竞赛 - 重现赛


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define rt 1, 1, n
#define ls o<<1
#define rs o<<1|1
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template inline void gmin(T1 &a, T2 b) { if (binline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n, m;
int a[1 << 17];	//a[i]维护线段树第i个节点的区间gcd值
int b[N];		//b[i]维护第i个位置的数值
int gcd(int x, int y)
{
	return y == 0 ? x : gcd(y, x%y);
}
void pushup(int o)
{
	a[o] = gcd(a[ls], a[rs]);
}
void build(int o, int l, int r)
{
	if (l == r)
	{
		a[o] = b[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(lson);
	build(rson);
	pushup(o);
}
int P, V;
void modify(int o, int l, int r)
{
	if (l == r)
	{
		a[o] = V;
		return;
	}
	int mid = (l + r) >> 1;
	P <= mid ? modify(lson) : modify(rson);
	pushup(o);
}
int L, R, G, TP;
int query(int o, int l, int r)
{
	if (l >= L && r <= R && a[o] % G == 0)return 0;
	if (l == r)return l;
	int mid = (l + r) >> 1;
	int tmp = 0;
	if (TP == 0)
	{
		if (R > mid)tmp = query(rson);
		if (tmp)return tmp;
		if (L <= mid)return query(lson);
	}
	else
	{
		if (L <= mid)tmp = query(lson);
		if (tmp)return tmp;
		if (R > mid)return query(rson);
	}
	return tmp;
}
vector< pair >lft, rgt;
LL cnt[1000010];int ans;
int dfn[1000010]; int tim;
void cal(int P, int sig, bool init = 0)
{
	lft.clear();
	rgt.clear();
	int i, p;
	//get lft
	TP = 0; p = P;
	for (i = p, G = b[p]; i >= 1; i = p, G = gcd(G, b[p]))
	{
		L = 1; R = i;
		p = query(rt);
		lft.push_back({ i - p, G });
	}
	//get rgt
	if (!init)
	{
		TP = 1; p = P;
		for (i = p, G = b[p]; i <= n; i = p, G = gcd(G, b[p]))
		{
			L = i; R = n;
			p = query(rt); if (p == 0) p = n + 1;
			rgt.push_back({ p - i, G });
		}
	}
	else rgt.push_back({ 1, b[p] });
	for (auto l : lft)
	{
		for (auto r : rgt)
		{
			LL num = (LL)l.first * r.first;
			int g = gcd(l.second, r.second);
			if (dfn[g] != tim)dfn[g] = tim, cnt[g] = 0;
			if (cnt[g] == 0)++ans;
			cnt[g] += num * sig;
			if (cnt[g] == 0)--ans;
		}
	}
}
void init()
{
	for (int i = 1; i <= n; ++i)cal(i, 1, 1);
}
int main()
{
	scanf("%d", &casenum);
	for (casei = 1; casei <= casenum; ++casei)
	{
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= n; ++i)scanf("%d", &b[i]);
		build(rt);
		++tim; 
		ans = 0;
		init(); 
		printf("Case #%d:\n", casei);
		for (int i = 1; i <= m; ++i)
		{
			scanf("%d%d", &P, &V);
			cal(P, -1);
			b[P] = V;
			modify(rt);
			cal(P, 1);
			printf("%d\n", ans);
		}
	}
	return 0;
}
/*
【题意】
有n(50000)个数a[](1e6范围)和q(50000)个操作
对于每个操作,我们会修改a[p]为v
问你每次修改之后会剩下多少个不同的区间连续gcd

【类型】
数据结构

【分析】
一个点的gcd值会影响所有包含其的区间
我们先预处理出n(n+1)个区间的gcd值,用一个cnt数组完成计数
然后对于修改,我们查询出包含这个点的所有区间段(这个可以通过在线段树上二分实现),在cnt数组中消除即可
然后再修改,再在cnt数组中加回去完成计数即可
复杂度的瓶颈在于我们合并区间时的左log个gcd区间和右log的gcd区间再求gcd导致的(log)^3

【时间复杂度&&优化】
O(nlognlognlogn)

【数据】
input
2
3 2
1 2 3
1 3
2 3
ans
3 1

input 
3 2
3 3 3
1 1
2 2
ans
2 3
*/


你可能感兴趣的:(题库-HDU,数据结构-线段树,二分)