hdu 4407

题意:给一个长度为n的序列,初始值为1~n;

对序列有以下两种操作;

1.查询[x,y]内与p互素的数的和;

2.修改第x数为c;

对于这道题,我们应该可以通过求【1,y】-【1,x-1】来得出答案;

我们先忽略操作2带来的影响,最后再通过暴力做法修改值即可(因为m范围只到1000,所以暴力不超时)

那么如何求区间答案呢;

我们有这样一个容斥定理:区间中与i不互质的个数=(区间中i的每个质因素的倍数个数)-(区间中i的每两个质因素的乘积的倍数个数)+

(区间中i的3个)-(4....)+(5....)-(6....)+(7.....)......  (偶数减,奇数加)

因为原序列为等差数列,我们可以得出前n项和公式,那么我们可以将问题转化为:区间内所有数的总和-区间内所有与p不互素的数的和

上面的定理求得是个数,但是我们可以转化为求和,比如素数是3,区间范围为1~10,那么就是3+6+9,而等比数列,假如首项为1,

则为n*(n+1)/2;而现在每一项都是某质数k的倍数,所以公式变为n*(n+1)/2*k,这样子就能根据定理一步步算出答案;

得出来之后,我们再走一遍暴力,将修改过的位置操作一下即可;

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 typedef long long LL;
  4 const int maxn=400010;
  5 int prime[maxn],cnt; ///保存素数,素数的个数
  6 bool book[maxn];
  7 map<int ,int >mp;
  8 vector<int> factor;
  9 int gcd(int a,int b)
 10 {
 11     if(!b) return a;
 12     else return gcd(b,a%b);
 13 }
 14 void Pri() ///线性筛素数
 15 {
 16     memset(book,0,sizeof(book));
 17     cnt=0;
 18     book[0]=book[1]=1;
 19     for(int i=2;i<maxn;i++)
 20     {
 21         if(!book[i]) prime[cnt++]=i;
 22         for(int j=0;j<cnt&&prime[j]*i<maxn;j++){
 23             book[i*prime[j]]=1;
 24             if(i%prime[j]==0) break;
 25         }  }}
 26 
 27 LL F(LL k,LL n){
 28     return n*(n+1)/2*k;
 29 }
 30 LL solve(int n) ///解决[1,n] 内与p互素的和
 31 {
 32     LL sum=F(1LL,n);  ///计算前n项和
 33     LL item=1<<factor.size(); ///素因子个数
 34     LL ans=0;
 35     for(int i=1;i<item;i++) ///二进制容斥,都差不多的
 36     {
 37         int num=0,x=1;
 38         for(int j=0;j<factor.size();j++){
 39             if(1&(i>>j)) {
 40                 num++;x*=factor[j];
 41             }
 42         }
 43         if(num&1) ans+=F(x,n/x);
 44         else ans-=F(x,n/x);
 45     }
 46     return sum-ans;
 47 
 48 }
 49 int main()
 50 { 
 51     int ncase;
 52     Pri();
 53     scanf("%d",&ncase);
 54     while(ncase--){
 55         mp.clear(); ///注意此处,每次测试案例都要清零
 56         int n,m;
 57         scanf("%d%d",&n,&m);
 58         while(m--){
 59             int choice;
 60             scanf("%d",&choice);
 61             int x,y,c,p;
 62             if(choice==1){
 63                 scanf("%d%d%d",&x,&y,&p);
 64                 if(x>y) swap(x,y);
 65                 factor.clear();  ///初始化
 66                 int item=p;
 67                 for(int i=0;i<cnt;i++) ///求出p的素因子
 68                 {
 69                     if(prime[i]>item) break;
 70                     if(item%prime[i]==0){
 71                         factor.push_back(prime[i]);
 72                     while(item%prime[i]==0)
 73                         item/=prime[i];
 74                     }   
 75                 }
 76     ///这是另外一种求素因子的方法
 77     //            for(int i=2;i*i<=item;i++){
 78     //                if(item%i==0){
 79     //                    factor.push_back(i);
 80     //                    while(item%i==0) item/=i;
 81     //                }
 82     //            }
 83                 if(item>1) factor.push_back(item);
 84                 LL sum=solve(y)-solve(x-1);
 85                 map<int ,int >::iterator it;///迭代器,用来访问元素
 86                 for(it=mp.begin();it!=mp.end();it++) ///查找操作2对结果有没有影响
 87                 {
 88                     int a=it->first,b=it->second;
 89                     if(a>y||a<x) continue;
 90                     if(gcd(a,p)==1) sum-=a; ///本来互素的要减掉
 91                     if(gcd(b,p)==1) sum+=b;///修改后互素的要加上
 92                 }  
 93                 printf("%lld\n",sum);
 94             }
 95             else{
 96                 scanf("%d%d",&x,&c);
 97                 mp[x]=c;
 98             }  
 99         }
100     }    
101     return 0;
102 }

 

posted @ 2020-02-22 10:37  古比  阅读(192)  评论(0编辑  收藏  举报