既然官方没有出题解,这里就说下比赛时候过的几题的题解。
A - Magic Number
讨论下 1000/x 的情况就可以了。
B - Battle Ships
dp[i][j] 表示 伤了i血,当前伤害为j。 dp[i][j] = min(dp[i][j], dp[i-t[k]*(j-l[k])][j-l[k]] + t[k]);
当然最后不能只去找 dp[L][j] 。 不造战舰的时间最后累加伤害到L。
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<climits> 6 #include<cstdlib> 7 #include<queue> 8 using namespace std; 9 #define N 710 10 #define inf 1000000000 11 typedef long long LL; 12 int t[N], l[N], dp[N][N]; 13 int main() { 14 int n, len; 15 while (scanf("%d%d", &n, &len) != EOF) { 16 int Max = 700; 17 for (int i = 0; i < n; ++i) 18 scanf("%d%d", &t[i], &l[i]); 19 for (int i = 0; i <= len; ++i) for (int j = 0; j <= Max; ++j) 20 dp[i][j] = inf; 21 dp[0][0] = 0; 22 for (int i = 0; i <= len; ++i) { 23 for (int j = 0; j <= Max; ++j) { 24 for (int k = 0; k < n; ++k) { 25 if (j >= l[k] && i >= t[k]*(j-l[k])) 26 dp[i][j] = min(dp[i][j], dp[i-t[k]*(j-l[k])][j-l[k]] + t[k]); 27 } 28 } 29 } 30 int ans = inf; 31 for (int i = 0; i <= len; ++i) for (int j = 1; j <= Max; ++j) { 32 int temp = (len-i)/j; 33 if ((len-i) % j) temp ++; 34 ans = min(ans, temp + dp[i][j]); 35 } 36 printf("%d\n", ans); 37 } 38 return 0; 39 }
C,D可以推出公式,反正我是推不出来。这里给各种神牛跪了。
E - Treasure Hunt I
树形背包,n很小, 偷懒的话 n^3 就可以过去
dp[u][j] = max(dp[u][j], dp[v][k]+dp[u][j-k-l*2]); l是边的值
F - Treasure Hunt II
每个大方向最多转一次,每次按照大方向走,另一边尽量贴近反方向,记录区间金币值sum[l, r] ,然后正反扫一次,统计最大的。
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<climits> 6 #include<cstdlib> 7 #include<queue> 8 using namespace std; 9 typedef long long LL; 10 #define N 100010 11 #define inf 1000000000 12 LL val[N], sum[N]; 13 int id[N]; 14 int n, p, m, t; 15 LL solve() { 16 memset(id, -1, sizeof(id)); 17 id[p] = p; 18 LL ans = 0; 19 int s1, s2; 20 s1 = s2 = p; 21 for (int cs = 0; cs < t; ++cs) { 22 if (s1 != 1) s1--; 23 else break; 24 if (s2 != n && s2+1-s1 <= m) s2++; 25 else if (s2-s1 > m && s2 != 1) s2--; 26 id[s1] = s2; 27 } 28 for (int i = 1; i <= p; ++i) { 29 if (p-i > t || id[i] == -1) continue; 30 LL temp = sum[p]-sum[i-1]; 31 int r = min(n, id[i]+t-(p-i)); 32 if (r < p) continue; 33 ans = max(ans, temp + sum[r]-sum[p]); 34 } 35 return ans; 36 } 37 int main() { 38 while (scanf("%d%d", &n, &p) != EOF) { 39 sum[0] = 0; 40 for (int i = 1; i <= n; ++i) { 41 scanf("%lld", &val[i]); 42 sum[i] = sum[i-1] + val[i]; 43 } 44 scanf("%d%d", &m, &t); 45 if (n == 1) { 46 printf("%lld\n", val[1]); 47 continue; 48 } 49 LL ans = solve(); 50 for (int i = 1; i <= n/2; ++i) 51 swap(val[i], val[n-i+1]); 52 for (int i = 1; i <= n; ++i) 53 sum[i] = sum[i-1] + val[i]; 54 p = n-p+1; 55 ans = max(ans, solve()); 56 printf("%lld\n", ans); 57 } 58 return 0; 59 }
G - Treasure Hunt III
麻烦的dp,题解在下一篇报告中,比赛没出。
H - Treasure Hunt IV
看这么多人过只好找规律了...
然后,推出公式水过。
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<climits> 6 #include<cstdlib> 7 #include<queue> 8 using namespace std; 9 typedef long long LL; 10 #define N 210 11 #define inf 1000000000 12 LL so(LL n) { 13 return n*(n+1)*4; 14 } 15 LL find(LL x) { 16 if (x == -1) return 0; 17 LL l = 0, r = 1518500249ll, t; 18 while (l <= r) { 19 LL mid = (l+r)/2; 20 if (so(mid)-1 < x) { 21 t = mid; 22 l = mid+1; 23 } 24 else r = mid-1; 25 } 26 x = x - so(t) + 1; 27 t++; 28 LL id = 2*t*t-t; 29 t *= 4; 30 if (x <= t) return id; 31 else return id + x - t; 32 } 33 int main() { 34 LL a, b; 35 while (scanf("%lld%lld", &a, &b) != EOF) { 36 printf("%lld\n", find(b)-find(a-1)); 37 } 38 return 0; 39 }
I - Information
好残暴的强连通,直接枚举删掉的点就行了 复杂度 n*m
J - Watashi's BG
把n个点平分成l, r两堆点。
2^l 和 2^r 枚举。排序后,一队数据从小到大,一队数据从大到小扫过去就可以了。
复杂度 2^15 * 15
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<climits> 6 #include<cstdlib> 7 #include<queue> 8 using namespace std; 9 #define N 710 10 #define inf 1000000000 11 typedef long long LL; 12 int val[N]; 13 int qu[2][1<<16]; 14 int main() { 15 int n, m; 16 while (scanf("%d%d", &n, &m) != EOF) { 17 for (int i = 0; i < n; ++i) 18 scanf("%d", &val[i]); 19 int a = n/2; 20 int b = n-a; 21 int top1, top2; 22 top1 = top2 = 0; 23 for (int i = 0; i < 1<<a; ++i) { 24 int temp = i, k = 0, s = 0; 25 while (temp) { 26 if (temp&1) s += val[k]; 27 temp >>= 1; 28 k++; 29 } 30 qu[0][top1++] = s; 31 } 32 int ans = 0; 33 for (int i = 0; i < 1<<b; ++i) { 34 int temp = i, k = a, s = 0; 35 while (temp) { 36 if (temp&1) s += val[k]; 37 temp >>= 1; 38 k++; 39 } 40 qu[1][top2++] = s; 41 } 42 sort(qu[0], qu[0]+top1); 43 sort(qu[1], qu[1]+top2); 44 int j = top2-1; 45 for (int i = 0; i < top1; ++i) { 46 for ( ; j >= 0; --j) { 47 if (qu[0][i] + qu[1][j] <= m) { 48 ans = max(ans, qu[0][i] + qu[1][j]); 49 break; 50 } 51 } 52 } 53 printf("%d\n", ans); 54 } 55 return 0; 56 }
K - Watermelon Full of Water
dp[i] 处理完前i天的最小花费。
当然直接转移会TLE,我是用线段树优化的,把能转移前K天到状态添加进去, 查找是找I~N天的转移状态的最小值。
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<climits> 6 #include<cstdlib> 7 using namespace std; 8 #define N 50010 9 #define inf 1000000000 10 typedef long long LL; 11 struct node { 12 int l, r; 13 LL num; 14 }tr[N*5]; 15 LL val[N], dp[N]; 16 int day[N]; 17 void build(int L, int R, int x) { 18 tr[x].l = L; 19 tr[x].r = R; 20 tr[x].num = 1000000000000000ll; 21 if (L == R) return ; 22 int mid = L+R >> 1; 23 build(L, mid, x<<1); 24 build(mid+1, R, x<<1|1); 25 } 26 void add(int id, int x, LL v) { 27 if (tr[x].l == tr[x].r) { 28 tr[x].num = min(tr[x].num, v); 29 return ; 30 } 31 int mid = tr[x].l + tr[x].r >> 1; 32 if (mid >= id) add(id, x<<1, v); 33 else add(id, x<<1|1, v); 34 tr[x].num = min(tr[x<<1].num, tr[x<<1|1].num); 35 } 36 LL find(int L, int R, int x) { 37 if (tr[x].l >= L && tr[x].r <= R) 38 return tr[x].num; 39 int mid = tr[x].l + tr[x].r >> 1; 40 if (mid >= R) return find(L, R, x<<1); 41 else if (mid < L) return find(L, R, x<<1|1); 42 else return min(find(L, mid, x<<1), find(mid+1, R, x<<1|1)); 43 } 44 int main() { 45 int n; 46 while (scanf("%d", &n) != EOF) { 47 for (int i = 1; i <= n; ++i) 48 scanf("%lld", &val[i]); 49 for (int i = 1; i <= n; ++i) 50 scanf("%d", &day[i]); 51 build(1, n, 1); 52 for (int i = 1; i <= n; ++i) { 53 add(day[i]+i-1 > n ? n : day[i]+i-1, 1, dp[i-1]+val[i]); 54 dp[i] = find(i, n, 1); 55 } 56 printf("%lld\n", dp[n]); 57 } 58 return 0; 59 }