1. 程式人生 > >8.21總結前日及今日

8.21總結前日及今日

並且 min http 意思 ons 就是 遞歸 ++ problem

估計拿不到打區域賽的名額了,還是有點憂傷,哎


8.21場鏈接


A題:

題意:

給出一個0~n-1的單詞表,問要組成一個長度為m,且單詞權值之和為k的單詞,的方案總數有多少種

解法:

其實就是問把k個球放到m個桶中(同中球數可以為空),且每個桶中的球數不超過n-1個,的方案總數。(仔細想一下,很有意思)

怎麽容斥? 總情況數 — 桶中球數超過n個的情況數即可,即:

技術分享圖片

當i等於0的時候,即是總的方案數,註意容斥的時候是奇數項為負,偶數項為正

 1 #include<cstdio>
 2 #include<cmath>
 3 using namespace std;
4 const int maxn = 1e6 + 7; 5 const long long mod = 998244353; 6 long long fac[maxn], inv[maxn]; 7 long long tmp[maxn]; 8 int n, m, k; 9 int t; 10 11 void init() 12 { 13 tmp[0] = tmp[1] = 1; 14 inv[1] = inv[0] = 1; 15 for (int i = 2; i < maxn; i++) 16 { 17 tmp[i] = (mod - mod / i) * tmp[mod % i] % mod;
18 inv[i] = inv[i - 1] * tmp[i] % mod; 19 } 20 fac[0] = 1; 21 for (int i = 1; i < maxn; i++) 22 fac[i] = (fac[i - 1] * i) % mod; 23 } 24 25 long long c(int n, int m) //c(n,m) 26 { 27 if (n < m) return 0; 28 return fac[n] % mod * inv[n - m] % mod * inv[m] % mod;
29 } 30 31 long long solve(int i) 32 { 33 return c(m + k - 1 - i * n, m - 1) % mod * c(m, i) % mod; 34 } 35 36 int main(int argc, char const *argv[]) 37 { 38 init(); 39 scanf("%d", &t); 40 while (t--) 41 { 42 scanf("%d%d%d", &n, &m, &k); 43 long long ans = 0; 44 for (int i = 0; i <= m && (i * n) <= k; i++) 45 { 46 if (i % 2) ans -= solve(i); 47 else ans += solve(i); 48 } 49 ans %= mod; 50 printf("%lld\n", (ans + mod) % mod); 51 } 52 return 0; 53 }

Uva12034

題意:兩匹馬比賽有三種比賽結果,n匹馬比賽的所有可能結果總數

解法:

設答案是f[n],則假設第一名有i個人,有C(n,i)種可能,接下來還有f(n-i)種可能性,因此答案為 ΣC(n,i)f(n-i)

另外這裏給出兩個求組合數的模板,盧卡斯定理的p是模數,並且要求是素數,第二個是遞推式,適合於n<2000的情況

 1 #include<cstdio>
 2 using namespace std;
 3 const int maxn = 1e3;
 4 const int mod = 10056;
 5 typedef long long ll;
 6 
 7 /*--------------------------盧卡斯定理取模-----------------------*/
 8 ll exp_mod(ll a, ll b, ll p) {
 9     ll res = 1;
10     while (b != 0) {
11         if (b & 1) res = (res * a) % p;
12         a = (a*a) % p;
13         b >>= 1;
14     }
15     return res;
16 }
17 
18 ll Comb(ll a, ll b, ll p) {
19     if (a < b)   return 0;
20     if (a == b)  return 1;
21     if (b > a - b)   b = a - b;
22 
23     ll ans = 1, ca = 1, cb = 1;
24     for (ll i = 0; i < b; ++i) {
25         ca = (ca * (a - i)) % p;
26         cb = (cb * (b - i)) % p;
27     }
28     ans = (ca*exp_mod(cb, p - 2, p)) % p;
29     return ans;
30 }
31 //Lucas定理對組合數取模
32 ll Lucas(int n, int m, int p) {
33     ll ans = 1;
34     while (n&&m&&ans) {
35         ans = (ans*Comb(n%p, m%p, p)) % p;
36         n /= p;
37         m /= p;
38     }
39     return ans;
40 }
41 
42 /*----------------------組合數遞推公式(適用n<2000)---------------------------*/
43 int C[maxn+10][maxn+10];
44 void Cal_C(int n) {
45     //傳遞的是一個二維的數組c
46     for (int i = 0; i <= n; i++){
47         C[i][0] = C[i][i] = 1;
48         for (int j = 1; j < i; j++)
49             C[i][j] = (C[i - 1][j - 1]%mod + C[i - 1][j]%mod)%mod;
50     }
51     return;
52 }
53 /*--------------------------------------------------------------------------*/
54 
55 int f[maxn+11];
56 void generate() {
57     Cal_C(maxn);
58     f[0] = 1;
59     for (int i = 1; i <= maxn; i++) {
60         f[i] = 0;
61         for (int j = 1; j <= i; j++)
62             f[i] = (f[i] + C[i][j] * f[i - j]) % mod;
63     }
64     return;
65 }
66 
67 int main() {
68     int T; scanf("%d", &T);
69     int kase = 1;
70     generate();
71     while (T--) {
72         int n; scanf("%d", &n);
73         printf("Case %d: %d\n", kase++, f[n]);
74     }
75     return 0;
76 }

Uva12230

題意:A,B相距D,A,B間有n條河,河寬Li,每條河上有一個速度為vi的船,在河山來回行駛,每條河離A的距離為pi,現在求從A到B時間的期望,步行速度始終為1

解法:首先如果全部步行則期望為D,現在每遇到一條河,求過河時間的期望,等待時間的區間為(0,2*L/v),船在每個地方都是等可能的,所以等待的期望就是(0 + 2*L/v) / 2 = L / v,又過河還要L / v,所以總的渡河期望值為2 * L / v,所以每遇到一條河拿D減去假設步行過河的期望L再加上實際過河期望2 * L / v即可,最後發現和p沒有卵關系,真開心~

 1 #include<iostream>
 2 using namespace std;
 3 
 4 int main() {
 5     int n, dis;
 6     int kase = 1;
 7     while (scanf("%d%d", &n, &dis)) {
 8         if (n == 0 && dis == 0)break;
 9         double ans = 0, suml = 0;
10         for (int i = 1; i <= n; i++) {
11             double p, l, v; cin >> p >> l >> v;
12             ans += 2 * l / v;
13             suml += l;
14         }
15         ans += (dis - suml);
16         printf("Case %d: %.3f\n\n", kase++, ans);
17     }
18     return 0;
19 }

Uva1639

題意:

有兩個盒子各有n個糖果(n<=200000),每天隨機選擇一個:選第一個盒子的概率是p(0 ≤ p ≤ 1),第二個盒子的概率為1-p,然後吃掉其中的一顆。直到有一天,隨機選擇一個盒子打開一看,沒糖了!現在請你計算另一個盒子裏剩下的糖果數量的期望值。

解法:

我們假設到第n天的時候取得是第1個盒子的糖,此時第2個盒子有i顆糖,則在此之前打開了n+(n-i)次盒子, 其中n次打開了第一個盒子,(n-i)次打開了第二個盒子,則概率是C(2n-i,n)*p^(n+1)*(1-p)^n-i。

由於n高達20w,所以二次項系數會非常大,而後面的概率會非常小,所以如果直接計算會爆精度,所以這裏我們用求對數的方法進行計算

 1 #include<iostream>
 2 #include<cmath>
 3 using namespace std;
 4 typedef long double lb;
 5 const int maxn = 2e5 + 5;
 6 long double logF[2 * maxn + 66];
 7 
 8 void generate() {
 9     //預處理出n!的log值
10     logF[0] = 0;
11     for (int i = 1; i <= maxn; i++)
12         logF[i] = logF[i - 1] + log(i);
13 }
14 // C(n,m) = n!/(m!(n-m)!)
15 long double logC(int n, int m) {
16     return logF[n] - logF[m] - logF[n - m];
17 }
18 
19 int main() {
20     int n; double p;
21     generate();
22     int kase = 1;
23     while (scanf("%d%lf", &n, &p)!=EOF) {
24         double ans = 0;
25         for (int i = 0; i <= n; i++) {
26             long double v1 = logC(2 * n - i, n) + (n + 1)*log(p) + (n - i)*log(1 - p);
27             long double v2 = logC(2 * n - i, n) + (n + 1)*log(1 - p) + (n - i)*log(p);
28             ans += (i*(exp(v1) + exp(v2)));
29         }
30         printf("Case %d: %.6lf\n", kase++, ans);
31     }
32     return 0;
33 }

Uva1640

題意:

統計兩個整數a,b之間各個數字(0~9)出現的次數,如1024和1032,他們之間的數字有1024 1025 1026 1027 1028 1029 1030 1031 1032 總共有10個0,10個1,3個3等等。

解法:

這類問題的一般步驟都是先用f(n,d)計算出0~n中d數字出現的次數,那麽答案就是f(b,d)-f(a-1,d)

下面程序中的註釋為(1,2974)的第一層(未遞歸)解釋,遞歸後同理

1-2974 拆分為1-2970 和 2971-2974

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 using namespace std;
 5 int l, r;
 6 int a[11], b[11];
 7 
 8 void solve(int n, int m) {
 9     int x = n / 10;//除了末位以外的                     x=297
10     int y = n % 10;//最後一位的數字範圍                  y=4
11     int tmp = x;
12     //計算小於10*x+1到10*x+y中個位數的個數,b[1-y]也就是(2971-2974)個位數
13     for (int i = 1; i <= y; i++)b[i] += m;
14     //計算10*x-10到10*x-1中個位數的總個數,296 0-296 9 個位數個數每個都是296個
15     for (int i = 0; i <= 9; i++)b[i] += m*x;
16     while (tmp) {
17     //10*x 到10*x+y非個位的個數,因為從0開始,所以*(y+1)就是非個位位數,當然還要乘m
18         b[tmp % 10] += m*(y + 1);     
19         tmp /= 10;
20     }
21     if (x) solve(x - 1, m * 10);
22 }
23 
24 int main() {
25     while (cin >> l >> r && (l || r)) {
26         memset(b, 0, sizeof(b));
27         if (l > r)swap(l, r);
28         solve(l - 1, 1);
29         for (int i = 0; i <= 9; i++)a[i] = b[i], b[i] = 0;
30         solve(r, 1);
31         cout << b[0] - a[0];
32         for (int i = 1; i <= 9; i++)cout << " " << b[i] - a[i];
33         cout << endl;
34     }
35     return 0;
36 }

Uva1641

題意:給出一個由 ‘ \ ‘,‘ . ‘, ‘ / ‘構成的圖形,問這個圖形的面積是多少

解法:對於’/‘或者’\‘,每個對面積的貢獻是0.5,對於‘.’來說從左到右,從上到下,判斷之前括號的個數是否為奇數即可,如果是奇數則說明在圖形內,反之亦然

 1 #include<cstdio>
 2 using namespace std;
 3 const int maxn = 1e2 + 5;
 4 char c[maxn][maxn];
 5 int h, w;
 6 int main() {
 7     while (scanf("%d%d", &h, &w)!=EOF) {
 8         double ans = 0.0;int cnt = 0;
 9         getchar();
10         for (int i = 0; i < h; i++)    {
11             for (int j = 0; j < w; j++) {
12                 scanf("%c", &c[i][j]);
13                 if (c[i][j] != .) ans += 0.5,cnt++;            
14                 else {if (cnt & 1)ans += 1;    }
15             }
16             getchar();
17         }
18         printf("%d\n",(int)ans);
19     }
20     return 0;
21 }

Uva1363

題意:

輸入正整數n和k(範圍均為1e9),求∑(k mod i),i從1~n

解法:

首先這道題直接暴力親測會超時。

之後我們寫幾組數據之後可以發現當k/i的商相同的時候他們的余數成一個等差數列,而且數列首相是q,公差是p,項的個數是余數/商。

具體寫法網上面有分情況討論的,但是較為繁瑣,這裏LRJ的板子感覺寫法就很精煉。

我們從左到右依次枚舉每一項i(核心思想是減少i的枚舉個數),計算出k除以這個數的商和余數, 如果這個商是0,說明此時的i已經大於k;如果不為0(即大於0),即來計算等差的數列的值。

如果k%i==0,則項的個數為0,計算和之後為0,其他情況就很正常了。

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 typedef long long ll;
 5 ll series_sum(int p, int d, int n) { return (ll)(2 * p - n*d)*(n + 1) / 2; }
 6 int main() {
 7     int n, k;
 8     while (scanf("%d%d", &n, &k) != EOF) {
 9         ll ans = 0, i = 1;
10         while (i <= n) {
11             int p = k / i, q = k%i;
12             int cnt = n - i;
13             if (p > 0)cnt = min(cnt, q / p);//計算項的個數,避免超出n的範圍
14             ans += series_sum(q, p, cnt);
15             i += cnt + 1;
16         }
17         printf("%lld\n", ans);
18     }
19     return 0;
20 }

8.21總結前日及今日