1. 程式人生 > 其它 >NOIP 模擬26

NOIP 模擬26

T1:

  n:1e14,於是對於題目中兩個式子,考慮避開n

很容易發現關鍵點在於a+b|a*b,對於如此簡潔的數學式

考慮其數學本質,對該式進行唯一分解考慮。

  這裡考慮最大公約數的唯一分解表示:即兩數的極

大公共質因子集之積,發現設(a,b)=d,a'=a/d,b'=b/d

則d即為兩數的極大公共質因子集之積,且(a',b')=1

於是原式轉化為a'+b'|d*a'*b',數論視角下發現若(a,b)=1

則a+b∤a*b於是發現使得原式成立的條件為a'+b'|d

  將所得式帶入a+b<=n -> (a'+b')*d<=n -> k*(a'+b')^2<=n

-> a'+b'<=√n -> d<=n/(a'+b')^2於是原問題轉化為求存在多少

(a,b,d)對,其中d<=n/(a'+b') , a'+b'<=√n

  觀察發現2式只有一個變數(a'+b')而1式存在兩個變數且

與2式相關於是考慮滿足2式的(a'+b')存在多少對,由於

(a',b')=1,限制條件不足無法計算,考慮增加限制條件:設

a'+b'=k,於是可得(a',k-a')=1 -> (a',k)=1,於是發現滿足條件的

(a',b')對的個數極為phi(k),求解√n內phi(k)並求出對應d,

乘法原理即可

程式碼如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define
I int 4 #define LL long long 5 #define B bool 6 const I MAXN = 1e7 + 3; 7 I tmp,cnt,prime[MAXN],euler[MAXN]; 8 LL n,res; 9 B notprime[MAXN]; 10 signed main () { 11 cin >> n; 12 tmp = sqrt (n); 13 for (I i(2);i <= tmp; ++ i) { 14 if (!notprime[i]) 15 prime[++cnt] = i, euler[i] = i - 1
; 16 for (I j(1);j <= cnt && i * prime[j] <= tmp; ++ j) { 17 notprime[i * prime[j]] = 1; 18 if (i % prime[j] == 0) { euler[i * prime[j]] = euler[i] * prime[j]; break; } 19 euler[i * prime[j]] = euler[i] * (prime[j] - 1); 20 } 21 res += n / (1ll * i * i) * euler[i]; 22 } 23 cout << res << endl; 24 }
View Code

T2:

  LIS板子,問題一線段樹,樹狀陣列維護皆可,問題二同樣,

樹狀陣列或線段樹維護LIS長度與問題一操作相同

  問題二另一種解法:發現問題本質為求LIS長度的個數,發現

對於LIS相同,其R值必定遞減,於是採用雙指標(R單調遞減)即可

程式碼如下(雙指標):

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define I int
 4 #define LL long long
 5 #define V void
 6 #define C char
 7 #define B bool
 8 const I MAXN = 1e5 + 7;
 9 const I mod = 123456789;
10 I n,typ,upd,res,tmp,maxn,num;
11 I R[MAXN],f[MAXN],g[MAXN];
12 vector <I> sta[MAXN],pre[MAXN];
13 struct TEL {
14     #define lowbit(x) (x & -x)
15     I c[MAXN];
16     inline V insert (I x,I y) { for ( ;x <= upd;x += lowbit(x)) c[x] = max (c[x],y); }
17     inline I secque (I x) {
18         I res(INT_MIN);
19         for ( ; x ;x -= lowbit(x)) res = max (res,c[x]);
20         return res;
21     }
22 }TEL;
23 signed main () { 
24     cin >> n >> typ;
25     for (I i(1);i <= n; ++ i)
26       cin >> R[i], upd = max (upd,++R[i]);
27     for (I i(1);i <= n; ++ i) {
28       tmp = TEL.secque (R[i] - 1) + 1;
29       TEL.insert (R[i],tmp);   
30       f[i] = tmp; maxn = max (maxn,tmp);
31     }
32     cout << maxn << endl;
33     if (typ) {
34         for (I i(0);i <= maxn; ++ i) sta[i].push_back(1),pre[i].push_back(0);
35         sta[0].push_back (INT_MIN); pre[0].push_back(1);
36         for (I i(1);i <= n; ++ i) {
37           while (sta[f[i] - 1][0] < sta[f[i] - 1].size () && R[sta[f[i] - 1][sta[f[i] - 1][0]]] >= R[i]) sta[f[i] - 1][0] ++ ; 
38           g[i] = pre[f[i] - 1][pre[f[i] - 1].size() - 1] - pre[f[i] - 1][sta[f[i] - 1][0] - 1];
39           sta[f[i]].push_back(i);
40           pre[f[i]].push_back((pre[f[i]][pre[f[i]].size() - 1] + g[i]) % mod);
41           if (!(f[i]^maxn)) (num += g[i]) %= mod;
42         }
43         cout << (num + mod) % mod << endl;
44     }    
45 }
View Code

T3:

  fib的顯著特徵為子結構性,即其差分序列仍為fib,拓展到樹上

子樹的結構完全相同,此時考慮DP思想,將問題轉化到子結構上

  注意的是,通常題目並不一定給出完全序列,即可能存在變形

此時為了簡化問題,我們可以考慮根據題目構造相似數列來更好貼合

題目,對於此題,觀察發現可以構造1,0,1,1,2,3。。。數列

  觀察fib樹,列舉深度確定貢獻與子樹數量利用fib規律計算

程式碼如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define I int
 4 #define LL long long
 5 const I MAXN = 5e3 + 3;
 6 const I mod = 123456789;
 7 I n;
 8 LL fib[MAXN],sum[MAXN],res[MAXN << 1];
 9 signed main () {
10     cin >> n;
11     fib[1] = fib[3] = sum[1] = sum[2] = 1; sum[3] = 2;
12     for (I i(4);i <= n; ++ i)
13       fib[i] = (fib[i - 1] + fib[i - 2]) % mod,sum[i] = (sum[i - 1] + fib[i]) % mod;
14     for (I i(2);i < n; ++ i) res[i] = sum[n - i] * fib[i + 1] % mod;
15     for (I i(1);i <= n; ++ i)
16       for (I j(1);j <= n; ++ j)
17         (res[i + j] += (sum[n - max(i,j) + 1] - 1) * fib[j + 1] % mod * fib[i]) %= mod;
18     for (I i(1);i <= n << 1; ++ i) cout << res[i] << ' ';
19 }
View Code