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.
题目大意:
有m次操作,操作1:求x~y之间与p互质的数的和,操作2:把第x个数字改为c,(注意是第x个数)
解题思路:
求区间和又有单点更新,可能会想到线段树,但是这个题考的是容斥原理
先考虑操作1:把p分解成素数因子,利用容斥原理我们可以得到1~x区间内与p互质数的和,所以answer就是sum(y)-sum(x),然后再单独判断一下x(因为x在两个区间里面重复计算了),这个是不考虑更新的;
对于操作2:我们把每一次的操作存在map里面(用数组或者pair封装的vector会错,因为可能存在一个点多次更新),m比较小(不会超时),所以每一次输出前遍历一遍map,判断是否需要更新,这个比较简单了
注意细节:在利用容斥原理计算时,比如在1~100内,求2*3的倍数的数字的和,100/6就是个数,也是最后一个数,sum=2*3(1+100/(2*3))*(100/(2*3))/2;也就是等差数列求和
最后就是求的sum会溢出,用long long存就好
代码还是很详细的..=v=..
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
map<int,int>mp;
int fac[200];
void update()
{
int a,b;
scanf("%d%d",&a,&b);
mp[a]=b;
}
int div(int p)//分解p
{
int cnt=0;
for(int i=2;i*i<=p;i++)
{
if(p%i==0)
fac[cnt++]=i;
while(p%i==0) p/=i;
}
if(p>1) fac[cnt++]=p;
return cnt;
}
bool co_prime(int n,int cnt)
{
for(int i=0;i<cnt;i++)
{
if(n%fac[i]==0)
return 0;
}
return 1;
}
ll get_mulsum(ll mul,int n)
{
ll sum=mul*(1+n/mul)*(n/mul)/2;
return sum;
}
ll rc(int x,int cnt)//得到[1,x]区间里面与p互质的数的和
{
ll sum1,sum2;
sum1=sum2=0;
for(int i=1;i<(1<<cnt); i++)
{
ll ones=0,mul=1;
for(int j=0;j<cnt;j++)
{
if(i & (1<<j))
{
mul*=fac[j];
ones++;
}
}
ll part_sum=get_mulsum(mul,x);
if(ones&1)
sum1+= part_sum;
else
sum1-= part_sum;
}
return (ll)(1+x)*x/2-sum1;
}
ll get_sum()
{
int x,y,p,cnt;
ll sum1,sum2,sum;
scanf("%d%d%d",&x,&y,&p);
cnt=div(p);
sum1=rc(x,cnt);//得到的是1~x与p互质的数的和
sum2=rc(y,cnt);//1~y的和
// printf("sum1=%lld,sum2=%lld\n",sum1,sum2);
sum=sum2-sum1;
if(co_prime(x,cnt)) sum+=x;//计算了两次
//scan operaton 2
map<int,int>::iterator it;
for(it=mp.begin();it!=mp.end();it++)
{
if(it->first >=x && it->first <=y)
{
if(co_prime(it->first,cnt))
sum-=it->first;
if(co_prime(it->second,cnt))
sum+=it->second;
}
}
return sum;
}
int main()
{
int t,n,m;
scanf("%d",&t);
while(t--)
{
int op;
scanf("%d%d",&n,&m);
mp.clear();
for(int i=0;i<m;i++)
{
scanf("%d",&op);
if(op==1)
printf("%lld\n",get_sum());
else
update();
}
}
return 0;
}