2016 ACM-ICPC CHINA-Final
题目链接:https://vjudge.net/contest/259560#overview
A题:签到,略
B题:
C题:
D题:二分+贪心,主要是check函数怎么写的问题。我们采用贪心的策略,我们假设现在二分的值是mid,就是说能组成mid个tower,那么,我们先选择mid个最小的 balls,这个是最优的。因为如果我放着小的 ball 不选而去选大的,那么下一层所要求的 ball 就更严格了。选取完 mid 个小的之后我们就可以再对每个小的选取剩下的最接近其2倍长度的球,为什么选最接近的呢?道理和上面选小的 ball 是一样的。这样我们的贪心算法基本上就成型了。代码如下:
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 typedef long long LL; 5 const int maxn = 3e5 + 100; 6 int T; 7 int n, k; 8 9 LL a[maxn]; 10 int vis[maxn]; 11 12 13 bool check(int mid) 14 { 15 for (int i = 1; i <= n; i++) vis[i] = 0; 16 for (int i = 1; i <= mid; i++) vis[i] = 1; 17 18 int st = 1, ed = mid, flag = 0, maxx = ed; 19 for (int i = 1; i <= k-1; i++) 20 { 21 for (int j = st; j <= ed; j++) 22 if (vis[j]) 23 { 24 int pos = lower_bound(a+maxx+1,a+1+n,2*a[j])-a; 25 //printf("%d %d\n",mid, a[pos]); 26 27 if (pos > n) { flag = 1; break; } 28 vis[pos] = 1; 29 maxx = max(maxx, pos); 30 } 31 if (flag) return false; 32 st = ed+1, ed = maxx; 33 } 34 35 return true; 36 } 37 38 39 int main(){ 40 scanf("%d",&T); 41 for(int t=1;t<=T;t++) 42 { 43 scanf("%d%d", &n, &k); 44 for (int i = 1; i <= n; i++) scanf("%lld", &a[i]); 45 sort(a+1, a+1+n); 46 47 int l = 0, r = n/k, ans = 0; 48 while(l <= r) 49 { 50 int mid = (l+r)/2; 51 if (check(mid)) 52 ans = mid, l = mid+1; 53 else r = mid-1; 54 } 55 56 printf("Case #%d: %d\n", t, ans); 57 } 58 return 0; 59 }
E题:简单贪心,这个题虽说是简答的贪心,但是过的人很少,因为这个题卡了精度,得使用 long double 才能过。
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <algorithm> 5 #include <cmath> 6 7 using namespace std; 8 const int maxn=100 + 10; 9 const int Max = 100000; 10 long double spend[maxn]; 11 12 int main(){ 13 int t; 14 scanf("%d", &t); 15 for (int ca = 1; ca <= t; ca++) 16 { 17 int n; 18 scanf("%d", &n); 19 for (int i = 1; i <= n; i++) 20 { 21 long double x, y; 22 char s; 23 cin >> x >> s >> y; 24 spend[i] = x/(y+x); 25 } 26 27 int ans = 0; 28 long double tot = 1; 29 sort(spend+1, spend+1+n); 30 31 for (int i = 1; i <= n; i++) 32 if (tot > spend[i]) tot -= spend[i], ans++; 33 34 printf("Case #%d: %d\n", ca, ans); 35 } 36 return 0; 37 }
F题:
G题:
H题:思路,这个题一眼看上去就像一个 容斥 + dp 什么的,当时觉得很复杂,就没想了。赛后补题发现这个题目十分有意思。我们先来化简一下这个式子:
第二步到第三步:实际上 g = 0 的 Ag 一直累加到 NM 这个过程就是 NM 的方格中任意放 K 个数的过程之和。所以就是:K^(NM)。
第三步到第四步:我们重新思考一下 Ag 是什么,Ag 表示 有 g 个 great 值的填法总数之和,那么 g*Ag 是不是就可以这样理解,我们考虑每一个格子为 great 格,而其他位置我们不考虑。其他位置随便填,统计有重复的情况正好是我们需要的,因为我们的 Ag 本来就要乘以 g。
代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long LL; 5 const LL MOD = 1e9 + 7; 6 7 LL qpow(LL a, LL b) { 8 LL ans = 1; 9 while(b) { 10 if(b&1) ans = ans*a%MOD; 11 a = a*a%MOD; 12 b >>=1; 13 } 14 return ans; 15 } 16 17 int main() { 18 int T, ca = 1; 19 scanf("%d", &T); 20 while(T--) { 21 int N, M, K; 22 scanf("%d%d%d", &N, &M, &K); 23 LL ans = qpow(K, N*M); 24 for(int i = 1; i < K; ++i) 25 ans = (ans + qpow(i, N-1+M-1) * qpow(K, (N-1)*(M-1)) % MOD * N * M % MOD) % MOD; 26 if(N == 1 && M == 1) ans = (ans+1)%MOD; 27 printf("Case #%d: %lld\n", ca++, ans); 28 } 29 return 0; 30 }
I 题:
J题:
K题:
L题:签到,略