hdu4407(容斥原理+最大公约数)

本文探讨了一个涉及数论与数据结构的问题:在指定区间内计算与特定数互质的数之和,并处理区间内的数值修改。通过容斥原理与线段树等数据结构,实现高效的算法解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Sum

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1490    Accepted Submission(s): 432


Problem Description
XXX is puzzled with the question below:

1, 2, 3, ..., n (1<=n<=400000) are placed in a line. There are m (1<=m<=1000) operations of two kinds.

Operation 1: among the x-th number to the y-th number (inclusive), get the sum of the numbers which are co-prime with p( 1 <=p <= 400000).
Operation 2: change the x-th number to c( 1 <=c <= 400000).

For each operation, XXX will spend a lot of time to treat it. So he wants to ask you to help him.
 

Input
There are several test cases.
The first line in the input is an integer indicating the number of test cases.
For each case, the first line begins with two integers --- the above mentioned n and m.
Each the following m lines contains an operation.
Operation 1 is in this format: "1 x y p".
Operation 2 is in this format: "2 x c".
 

Output
For each operation 1, output a single integer in one line representing the result.
 

Sample Input
  
1 3 3 2 2 3 1 1 3 4 1 2 3 6
 

Sample Output
  
7 0
 

Source
 

Recommend
zhoujiaqi2010
        本题初始化一1到n的序列,然后给出两种操作。
                         1,x,y,p表示在[x,y]的区间范围内求与p互质的数的和。
                         2.,x,c表示修改x处的数为c。
        本题n<=400000,m<=1000.对于求[x,y]的区间范围内求与p互质的数的和。
若直接求互质的数,可能时间复杂度不允许。可以求与之不互质的数的和。先算出p的所有质因子,然后就是等差数列求和了。由于会出现重复,例如2,4,6,8,10……,3,6,9……的公共部分6.所有必须利用容斥原理。含奇数个质因数的加,含偶数个质因数的减。然后用区间[x,y]总和减去区间[x,y]与p不互质的数的和。注意本题还有个修改x处的值的操作。 本操作可以借助map容器,每次修改map[x]=c.然后在1操作时先遍历map容器;若p->first与p互质,则减去p->first;若p->second与p互质,则加p->second。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
using namespace std;

const int MAXN=700;
int Prim[MAXN];
int Fac[MAXN];
int num_prim,n,num_fac;

//求最大公约数
int Grd(int a,int b)
{
	return a%b?Grd(b,a%b):b;
}

//求所有的质数
void Prime()
{
	int i,j,k;
	num_prim=0;
	for(i=2;i<MAXN;i++)
	{
		k=(int)sqrt(i*1.0);
		for(j=2;j<=k;j++)
		{
			if(i%j==0)break;
		}
		if(j>k)Prim[num_prim++]=i;
	}
}

//打印所有的质数
void Prim_print()
{
	int i;
	for(i=0;i<num_prim;i++)
	{
		if(i&&i%10==0)printf("\n");
		printf("%d  ",Prim[i]);
	}
	printf("\n*******************\n");
}


__int64 DFS(int en,__int64 tmp,int s,bool op)
{
	int i;
	__int64 ans=0,num=en/tmp;
	if(op)ans+=((__int64)(num*tmp+tmp)*num)>>1;
	else ans-=((__int64)(num*tmp+tmp)*num)>>1;
	for(i=s+1;i<=num_fac;i++)
	{
		ans+=DFS(en,tmp*Fac[i],i,!op);
	}
	return ans;
}

__int64 Solve(int en,int p)
{
	int i,j;
	if(en==0)return 0;
	__int64 ret=0;
	int k=sqrt(p*1.0);
	num_fac=0;

	//求质因数
	for(i=0;Prim[i]<=k;i++)
	{
		if(p%Prim[i]==0)
		{
			Fac[++num_fac]=Prim[i];
		}
		while(p%Prim[i]==0)
		{
			p/=Prim[i];
		}
		if(1==p)break;
	}
	if(p!=1)
	{
		Fac[++num_fac]=p;
	}

	/*for(i=1;i<=num_fac;i++)
	printf("%I64d ",Fac[i]);
	printf("\n11111111111111\n");*/

	//用容斥原理求和
	/************非递归写法*****************
	int id=1<<num_fac;
	for(i=1;i<id;i++)
	{
		__int64 tmp=1;
		int cnt=0;
		for(j=0;j<num_fac;j++)
		{
			if(i&1<<j)
			{
				tmp*=Fac[j+1];
				cnt++;
			}
		}
		//	printf("tmp=%I64d\n",tmp);
		__int64 t;
		t=en/tmp;
		tmp=((tmp+t*tmp)*t)>>1;
		if(cnt&1)
			ret+=tmp;
		else ret-=tmp;
		}
		**********************************/


	/*******************************************
	递归求法
    *****************************************/
	for(i=1;i<=num_fac;i++)
	   ret+=DFS(en,Fac[i],i,true);
	//printf("ret=%d\n",ret);

	return ret;
}

map<int,int>Mat;
map<int,int>::iterator iter;

int main()
{
	int m,a,b,p,op,cas;
	Prime();
	//Prim_print();
	cin>>cas;
	while(cas--)
	{
		scanf("%d%d",&n,&m);
		Mat.clear();
		while(m--)
		{
			scanf("%d",&op);
			if(1==op)
			{
				scanf("%d%d%d",&a,&b,&p);
				__int64 ans=0;
				for(iter=Mat.begin();iter!=Mat.end();iter++)
				{
					if(iter->first>=a&&iter->first<=b)
					{
						ans-=(__int64)Grd(iter->first,p)==1?iter->first:0;
						ans+=(__int64)Grd(iter->second,p)==1?iter->second:0;
					}
				}
				ans+=((__int64)(a+b)*(b-a+1)/2-(Solve(b,p)-Solve(a-1,p)));
				printf("%I64d\n",ans);
			}
			else
			{
				scanf("%d%d",&a,&b);
				Mat[a]=b;
			}
		}
	}
	system("pause");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值