2019.10.28 CSP%您賽第四場t3

  • 2019 年 10 月 28 日
  • 筆記

我寫不動前兩個了。
原諒一下。
____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________

【題目描述】
(varphi)函數是數論中非常常用的函數。對於正整數 x,(varphi)(x) 表示不超過 x 的所有正整數
與 x 互質的個數。
現在我們對它進行一次拓展:對於正整數 x,y,定義 (varphi)(x,y) 表示不超過 y 的所有正
整數與 x 互質的個數。
現在我們給定正整數 n 和 m,對於所有不超過 n 的正整數 i,求 (varphi)(i,m)。
【輸入格式】
從文件 phi.in 中讀入數據。
輸入僅一行兩個正整數 n 和 m。
【輸出格式】
輸出到文件 phi.out 中。
輸出 n 行,每行一個整數。第 i 行表示 (varphi)(i,m)。
【樣例輸入】
11 10
【樣例輸出】
10
5
7
5
8
3
9
5
7
4
10

以上題面。
順便補一句,以上演算法可接受的複雜度是(O(nsqrt n))
容易知道,我們枚舉所有(ileq n)時,時間複雜度是(O(n))。所以對於每個數的判斷,我們可接受的複雜度大約是(O(sqrt n))
考慮原來做過的題類似的做法。對於一個數(i),與這個數互質的數的本質實際上是不存在與(i)相同的質因子。所以對於每個(i),我們用(O(sqrt n))的複雜度求出其所有質因子作為預處理;
而對於每一個求出的質因子,任何含有該質因子的數都不能貢獻到答案上。但是由於存在同時具有好幾個該數質因子的情況,考慮容斥原理:
舉個例子,如果一個數有3個質因子,設其為(a_1,a_2,a_3),則(ans=m-m/a1-m/a2-m/a3+m/a1/a2+m/a2/a3+m/a1/a3-m/a1/a2/a3)。其中$m是最大取值個數。
同理,設一個數有k個質因子,則容易知道:
(ans=m-m/(所有奇數個a_k相乘的可能)+m/(所有偶數個a_k)相乘的可能)
容易證明。當我們減去所有含有單個質因子的數時,我們多減去了所有嚴格含有兩個質因子的數;再加上嚴格含有兩個質因子的數,又多加了嚴格含有三個質因子的數;……以此類推,其實是(k)階維恩圖上的容斥原理。
所以我們可以用一個二進位數的每一位代表該質因子是否被選中。若選中了偶數個質因子,則加上;否則減去。
上程式碼:

#include<iostream>  #include<cstdio>  #include<cstring>  #include<cctype>  #include<cmath>  #define rep(i,a,n) for(int i=a;i<=n;i++)  #define dep(i,n,a) for(int i=n;i>=a;i--)  #define int long long  using namespace std;  int n,m,prime[100050],idx,ans,book_prime[100050],idx1;  bool is_prime[100050],book[100050],mark[100050];  inline int read()  {      int x=0,f=1;      char ch=getchar();      while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}      while(isdigit(ch))x=x*10+ch-'0',ch=getchar();      return x*f;  }  signed main()  {      memset(prime,-1,sizeof prime);      n=read(),m=read();      rep(i,1,n)      {          memset(book,0,sizeof book);          memset(mark,0,sizeof mark);          ans=m;          idx1=0;          int temp=i;          int qqqqqq=sqrt(i);          rep(j,2,qqqqqq)          {              if(temp%j==0)              {                  book_prime[++idx1]=j;                  while(temp%j==0)                      temp/=j;              }          }          if(temp>1)              book_prime[++idx1]=temp;          int maxn=(1<<idx1)-1;          rep(j,1,maxn)          {              int cnt=0;              int tmp=j;              int base=1;              int mul=1;              while(tmp)              {                  if(tmp&1)                      ++cnt,mul*=book_prime[base];                  ++base;                  tmp>>=1;              }              if(cnt&1)ans-=m/mul;              else ans+=m/mul;          }          printf("%lldn",ans);      }      return 0;  }