A、Stones
题意:
给出三种石头分别$a$,$b$,$c$个,每次拿石头的时候只能拿$1$个第一种石头和$2$个第二种石头,或者$1$个第二种石头和$2$个第三种石头,求最多能拿到多少?
题解:
先考虑后一种方案:这样子一共拿了$min(b, c/2)*3$个石头,然后剩下的石头再取$min((b-c/2)/2,a)*3$.
AC代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 int main() 4 { 5 int n; 6 scanf("%d", &n); 7 while (n--) 8 { 9 int a, b, c; 10 scanf("%d%d%d", &a, &b, &c); 11 int ans = 0; 12 while (b > 0 && c > 1) 13 { 14 ans += 3; 15 b -= 1; 16 c -= 2; 17 } 18 while (b > 1 && a > 0) 19 { 20 ans += 3; 21 b -= 2; 22 a -= 1; 23 } 24 printf("%d ", ans); 25 } 26 return 0; 27 }
B、Alice and the List of Presents
题意:
给出无限个$n$种东西放进$m$个包且每个包中不出现重复元素的方案数,包里面可以不放东西。
题解:
考虑每个包里面都可以放或者不放某东西,一共$2^m-1$种,然后一共有$n$个物品,所以就是$(2^m-1)^n$种。
AC代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const ll mod = 1e9 + 7; 5 ll pow(ll a, ll b, ll p) 6 { 7 ll res = 1; 8 while (b) 9 { 10 if (b & 1) 11 res = res * a % p; 12 a = a * a % p; 13 b >>= 1; 14 } 15 return res; 16 } 17 int main() 18 { 19 ll n, m; 20 scanf("%lld%lld", &n, &m); 21 printf("%lld", pow((pow(2, m, mod) - 1ll + mod) % mod, n, mod)); 22 return 0; 23 }
C、Labs
题意:
定义$f(X,Y)$是$X$集合中$a$,和$Y$集合中的$b$满足$a>b$的数对的数量。给出一个$n imes n$的矩阵,元素是$1~n^2$,求出一种填数方式,使得对于所有的行,最大的$f(X_i,X_j)$最小。
题解:
考虑要最大的最小,首先发现因为所有的数都不一样,所以$f(X,Y)+f(Y,X)=n*n$,则我们需要找到一种分法,使得所有的行之间的$max(f(X,Y),f(Y,X))$等于$frac {n*n}{2}$。所以,我们可以把这$n*n$个数按顺序分成$n$组,然后就第一组选$max$,第二组选$min$,第三组选$max$交替选够$n$个数,构成第一行,然后第二行从$min$选起,交替选够$n$个数,以此类推,观察矩阵的数可以发现规律,按规律写出代码即可。
AC代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 3e2 + 5; 4 int ans[N][N]; 5 int main() 6 { 7 int n; 8 scanf("%d", &n); 9 for (int i = 1; i <= n; ++i) 10 for (int j = 1; j <= n; ++j) 11 ans[i][j] = i + n * (j - 1); 12 for (int i = 2; i <= n; i += 2) 13 for (int j = 1; j <= n / 2; ++j) 14 swap(ans[j][i], ans[n - j + 1][i]); 15 for (int i = 1; i <= n; ++i) 16 for (int j = 1; j <= n; ++j) 17 printf("%d%c", ans[i][j], " "[j == n]); 18 return 0; 19 }
D、Alice and the Doll
题意:
给出一个玩偶,它只能向前和向右,这个玩偶在$(1,1)$开始,走一个有障碍物的$n*m$的方格图,求能不能走完所有不是障碍物的格子。
题解:
理论上直接模拟就行,但是这样子时间复杂度是$O(n*m)$,所以我们预处理出每一行每一列所有障碍物,排好序,这样子就可以用二分查找找出前面的第一个障碍物。这样子时间就降到$O(nlogn)$了。
AC代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 1e5 + 5; 4 typedef long long ll; 5 vector<int> G1[N]; 6 vector<int> G2[N]; 7 int main() 8 { 9 int n, m, k; 10 scanf("%d%d%d", &n, &m, &k); 11 for (int i = 1; i <= k; ++i) 12 { 13 int a, b; 14 scanf("%d%d", &a, &b); 15 G1[a].push_back(b); 16 G2[b].push_back(a); 17 } 18 for (int i = 1; i <= n; ++i) 19 sort(G1[i].begin(), G1[i].end()); 20 for (int i = 1; i <= m; ++i) 21 sort(G2[i].begin(), G2[i].end()); 22 bool f = 0; 23 ll ans = 1; 24 int x = 1, y = 1; 25 int up = 1, down = n, left = 1, right = m; 26 int dir = 0; 27 while (1) 28 { 29 int ny, nx; 30 if (dir == 0) //右方向 31 { 32 int pos = lower_bound(G1[x].begin(), G1[x].end(), y) - G1[x].begin(); 33 nx = x; 34 if (pos == G1[x].size()) 35 ny = right; 36 else 37 ny = min(right, G1[x][pos] - 1); 38 up = x + 1; 39 } 40 else if (dir == 1) //下方向 41 { 42 int pos = lower_bound(G2[y].begin(), G2[y].end(), x) - G2[y].begin(); 43 ny = y; 44 if (pos == G2[y].size()) 45 nx = down; 46 else 47 nx = min(down, G2[y][pos] - 1); 48 right = y - 1; 49 } 50 else if (dir == 2) 51 { 52 int pos = lower_bound(G1[x].begin(), G1[x].end(), y) - G1[x].begin() - 1; 53 nx = x; 54 if (pos < 0) 55 ny = left; 56 else 57 ny = max(left, G1[x][pos] + 1); 58 down = x - 1; 59 } 60 else if (dir == 3) 61 { 62 int pos = lower_bound(G2[y].begin(), G2[y].end(), x) - G2[y].begin() - 1; 63 ny = y; 64 if (pos < 0) 65 nx = up; 66 else 67 nx = max(up, G2[y][pos] + 1); 68 left = y + 1; 69 } 70 if (nx == x && ny == y && f) 71 break; 72 dir = (dir + 1) % 4; 73 ans += abs(nx - x) + abs(ny - y); 74 x = nx; 75 y = ny; 76 f = 1; 77 } 78 if (ans == (ll)n * m - k) 79 printf("Yes "); 80 else 81 printf("No "); 82 return 0; 83 }
E、Alice and the Unfair Game
题意:
给出$n$的盒子,其中有一个盒子有宝物,$A$会给出一个位置$pos$,如果猜对了就赢了,为了防止赢,在$A$猜了之后,宝物可以向相邻的盒子转移,设开始时宝物在$x$,猜$m$次之后在$y$,求$(x,y)$的数量。
题解:
这样的问题通pos_i常建图解决,实际上就是求从起点开始走了$m$步之后到达的位置的集合。但是这里因为它只能向相邻盒子走,所以我们直接扩展出$(m+2) imes n$的矩阵,$(i,pos_i)$表示障碍,找到从$(0,x)$走到$(m+1,y)$的路径数就是答案,显然,找出从一个点贪心向左走或者贪心向右走的区间后,区间内的所有位置都可达。然后可以得到$l_{a_i+i}=l_{a_i+i+1}+1$,$r_i$同理,因为考虑到可能存在$i$和$j$使得$i+a_i=j+a_j$,则需要倒序枚举。然后再对每一个点$(0,x)$求出在第$m+1$行的范围,累加即可。
AC代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 1e5 + 5; 4 typedef long long ll; 5 int a[N], l[N << 2], r[N << 2]; 6 const int offset = 1e5; 7 int main() 8 { 9 int n, m; 10 scanf("%d%d", &n, &m); 11 for (int i = 1; i <= m; ++i) 12 scanf("%d", &a[i]); 13 if (n == 1) 14 return printf("0 "), 0; 15 for (int i = m; i >= 1; --i) 16 { 17 l[a[i] - i + offset] = l[a[i] - i - 1 + offset] + 1; 18 r[a[i] + i + offset] = r[a[i] + i + 1 + offset] + 1; 19 } 20 ll ans = 0; 21 for (int i = 1; i <= n; ++i) 22 { 23 int rr = min(n, i + m + 1 - l[i + offset]); 24 int ll = max(1, i - m - 1 + r[i + offset]); 25 ans += rr - ll + 1; 26 } 27 printf("%lld ", ans); 28 return 0; 29 }