HDU4407 – Sum(容斥原理+分解质因数)

HDU4407

【题意】给出N(<=400000)个数字1~N,a[i] = i组成的数列;m(<=1000)个操作:

1 x y p:查询区间[x,y]中与p互质的数的所有数的和;

2 x y:把a[x]修改为y

【分析】首先,可以把修改操作孤立,由于总操作不超过1000个,那么每次询问分成两部分:1.没有任何修改的[x,y]的答案;2.处理之前所有修改;这样就只要求出没有任何修改的值就解决了。

这样问题变成了求[x,x+1,x+2…y-1,y]中与p互质的数的和。其实和欧拉phi函数很像,而那个其实是可以用容斥原理+分解质因数解决的。

把p分解质因数,去掉[x,y]中所有是p的质因数的倍数的数字全部去掉即可,这步可以用等差数列求和公式O(1)算出,但是直接计算去掉会有很多重复部分,这时候就要使用容斥原理,求出每个质因数的搭配,a,b,c,d,a*b,a*c,a*d…a*b*c*d;对于每个搭配有奇数个前面符号为正否则为负。

由于1~400000的数分解质因素不会超过6种,所以容斥的复杂度为n* 2^n很小可以忽略。

总的复杂度为m*m+m*sqrt(ai)

【AC CODE】561ms

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define per(i,n,a) for(int i = n; i >= a; i--)
#define clc(a,b) memset(a,b,sizeof(a))
const int INF = 0x3f3f3f3f, MAXN = 1000+10;

int gcd(int a, int b)
{
	while(b)
	{
		a %= b;
		if(a < b) swap(a,b);
	}
	return a;
}
vector<int> p;
void get_p(int n)
{
	int m = sqrt(n+0.5);
	p.clear();
	for(int i = 2; i <= m; i++)
	{
		if(n % i == 0)
		{
			p.push_back(i);
			while(n % i == 0)
				n /= i;
		}
	}
	if(n > 1) p.push_back(n);
}
inline LL count(LL x, LL y, LL v)
{
	LL n = y/v-(x-1)/v,a = ((x-1)/v+1)*v;
	if(a > y) return 0;
	return n*a+n*(n-1)*v/2;
}
LL sloved(LL x, LL y, int c)
{
	get_p(c);
	int sz = p.size(),all = 1<<sz;
	LL ans = 0;
	rep(s,1,all)
	{
		int bit = 0,tmp = 1;
		rep(i,0,sz)
		{
			if(s&(1<<i))
			{
				bit++;
				tmp *= p[i];
			}
		}
		if(bit&1) ans += count(x,y,tmp);
		else ans -= count(x,y,tmp);
	}
	return (x+y)*(y-x+1)/2-ans;
}
map<int,int> cg;

LL query(int x, int y, int c)
{
	LL ans = sloved(x,y,c);
	for(auto it = cg.lower_bound(x); it != cg.end() && it->first <= y; it++)
	{
		int u = it->first, v = it->second;
		if(1 == gcd(u,c) && 1 != gcd(v,c))
			ans -= u;
		else if(1 == gcd(u,c) && 1 == gcd(v,c))
			ans -= u,ans += v;
		else if(1 != gcd(u,c) && 1 == gcd(v,c))
			ans += v;
	}
	return ans;
}
int main()
{
#ifdef SHY
	freopen("d:\\1.txt", "r", stdin);
#endif
	int t;
	scanf("%d", &t);
	while(t--)
	{
		int n,q;
		scanf("%d %d", &n, &q);
		cg.clear();
		while(q--)
		{
			int op,x,y;
			scanf("%d %d %d", &op, &x, &y);
			if(1 == op)
			{
				int v;
				scanf("%d", &v);
				printf("%I64d\n", query(x,y,v));
			}
			else cg[x] = y;
		}
	}
	return 0;
}

 

发表评论

邮箱地址不会被公开。 必填项已用*标注

请输入正确的验证码