hdu 4407 Sum [容斥原理] 超时的同学看过来

Sum

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

 

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

2012 ACM/ICPC Asia Regional Jinhua Online

 

 

Recommend

zhoujiaqi2010   |   We have carefully selected several similar problems for you:  6396 6395 6394 6393 6392 算法分析:

题意:

给一个长度为n的序列,序列由1~n依次组成。 
对序列执行两种操作: 
1.查询[x,y]内与p互素的数的和; 
2.修改第x数为c.

分析:

与以往容斥原理不同,这回不叫计数,而叫求和了,想了好久,上网是一个等比数列,公式套用就行。

对于操作二,想到只能暴力搜索,操作数不多,能过。Vector保存修改的值话需要判重,map就不用了。

 

最坑爹的是map做的代码超时,这无所谓,但它竟然因为gcd()函数,把它改成int,760ms过。

我知道了,刚才又交了一发,把map<int,int>改为long long就对了。也就是说,你查找的数据必须和map类型中对应。

但vector做的long long 却过了,玄学啊

代码实现:

Map

#include<cstdio>  
#include<cstring>  
#include<cstdlib>  
#include<cctype>  
#include<cmath>  
#include<iostream>  
#include<sstream>  
#include<iterator>  
#include<algorithm>  
#include<string>  
#include<vector>  
#include<set>  
#include<map>  
#include<stack>  
#include<deque>  
#include<queue>
using namespace std;
typedef long long ll;
long long ans=0,sum=0,r;
ll fac[105];
map<int,int>Map;
int gcd(int a,int b)
{//求最大公约数
    return b?gcd(b,a%b):a;
}
void factor(ll m)
{
    sum = 0;
    ll tmp = m;
    for(ll i = 2; i*i<=tmp; i++)    //官方版的唯一分解定理
    if(tmp%i==0)
    {
        fac[sum++] = i;
        while(tmp%i==0) tmp /=  i;
    }
    if(tmp>1) fac[sum++] = tmp;
}
ll getsum(ll n)
{//等差数列求和公式
    return (n+1)*n/2;//注意(n+1)/2*n这样不对
}
void dfs(int i,int cnt,int tmp)//i代表数组的下标
{                              //cnt代表几个元素的最小公倍数   
       
    if(cnt&1)                   //容斥原理
        ans+=(ll)tmp*getsum(r/tmp);
    else
        ans-=(ll)tmp*getsum(r/tmp);
	
    for(int j=i+1;j<sum;j++)
        dfs(j,cnt+1,tmp*fac[j]);
}
long long sub(long long ans,int a,int b,int p)///修改
{
    map<int,int>::iterator it;
    for(it=Map.begin();it!=Map.end();it++)
    {
        int f1=gcd(p,it->second);
        int f2=gcd(p,it->first);
        if(it->first<=b&&it->first>=a)
        {
            if(f1==1) ans+=it->second;
            if(f2==1) ans-=it->first;
        }
    }
    return ans;
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {   
		Map.clear();
      
        int n,m;
        ll sum1=0;
        scanf("%d%d",&n,&m);
	    for(int i=1;i<=m;i++)
		{
			int x,y,a,b;
			scanf("%d",&a);
			
			if(a==2)
			{
				scanf("%d%d",&x,&b);
			    Map[x]=b;
			}
			else
			{
				sum1=0;
				sum=0;
				ans=0;
				
				scanf("%d%d%d",&x,&y,&b);
				factor(b);
				r=x-1;
			    for(int j=0;j<sum;j++)
				{
					dfs(j,1,fac[j]);
				}
				
				sum1=getsum(x-1)-ans;
				
				r=y;
				ans=0;
				for(int j=0;j<sum;j++)
				{
					dfs(j,1,fac[j]);
				}
				sum1=getsum(y)-ans-sum1;
				cout<<sub(sum1,x,y,b)<<endl;
			}
		}
    }
} 

Vector

#include<cstdio>  
#include<cstring>  
#include<cstdlib>  
#include<cctype>  
#include<cmath>  
#include<iostream>  
#include<sstream>  
#include<iterator>  
#include<algorithm>  
#include<string>  
#include<vector>  
#include<set>  
#include<map>  
#include<stack>  
#include<deque>  
#include<queue>
using namespace std;
typedef long long ll;
long long ans=0,sum=0,r;
ll fac[105];
ll gcd(ll a,ll b)
{//求最大公约数
    return b?gcd(b,a%b):a;
}
void factor(ll m)
{
    sum = 0;
    ll tmp = m;
    for(ll i = 2; i*i<=tmp; i++)    //官方版的唯一分解定理
    if(tmp%i==0)
    {
        fac[sum++] = i;
        while(tmp%i==0) tmp /=  i;
    }
    if(tmp>1) fac[sum++] = tmp;
}
ll getsum(ll n)
{//等差数列求和公式
    return (n+1)*n/2;//注意(n+1)/2*n这样不对
}
void dfs(int i,int cnt,int tmp)//i代表数组的下标
{                              //cnt代表几个元素的最小公倍数   
       
    if(cnt&1)                   //容斥原理
        ans+=(ll)tmp*getsum(r/tmp);
    else
        ans-=(ll)tmp*getsum(r/tmp);
	
    for(int j=i+1;j<sum;j++)
        dfs(j,cnt+1,tmp*fac[j]);
}
struct node
{
	int x,c;
}w;
vector<node> v;
int main()
{
    int T;
    cin>>T;
    int t=0;

    while(T--)
    {   
    	v.clear();
        t++;
        int n,m;
        ll sum1=0;
        scanf("%d%d",&n,&m);
	    for(int i=1;i<=m;i++)
		{
			int x,y,a,b;
			scanf("%d",&a);
			
			if(a==2)
			{
				
				scanf("%d%d",&w.x,&w.c);
				int flag=1;
				//判断该位置是否已经修改过了
				 for(int j=0;j<v.size();j++)
				 	if(v[j].x==w.x)
				 {
				 	flag=0;
				 	v[j].c=w.c;
				 	break;
				 }
				if(flag) v.push_back(w);
			}
			else
			{
				sum1=0;
				sum=0;
				ans=0;
				
				scanf("%d%d%d",&x,&y,&b);
				factor(b);
				r=x-1;
			    for(int j=0;j<sum;j++)
				{
					dfs(j,1,fac[j]);
				}
				sum1=getsum(x-1)-ans;
				
				
				r=y;
				ans=0;
				for(int j=0;j<sum;j++)
				{
					dfs(j,1,fac[j]);
				}
				sum1=getsum(y)-ans-sum1;
				
				for(int j=0;j<v.size();j++)
				{
					if(v[j].x<=y&&v[j].x>=x)
					{
						if(gcd(b,v[j].x)==1)
						sum1-=v[j].x;
					   if(gcd(b,v[j].c)==1)
						sum1+=v[j].c;
					}
				}
				cout<<sum1<<endl;
			}
		}
    }
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值