1. 程式人生 > >【HDU5930 2016CCPC東北地區大學生程式設計競賽

【HDU5930 2016CCPC東北地區大學生程式設計競賽


#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
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 <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 5e4 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline 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<int, int> >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
*/