Problem A Secret of Chocolate Poles
Solved.
题意:有两种物品长度为1以及k, 求在长度为l 的箱子里放置物品的方案数, 每两个物品间有一个长度为1的空白
思路:$dp[i][0/1]$ $i$表示当前高度, $0/1$表示放不放
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 typedef long long ll; 6 7 const int maxn = 1e3 + 10; 8 9 int l, k; 10 ll dp[maxn][2]; 11 12 int main() 13 { 14 while(~scanf("%d %d", &l, &k)) 15 { 16 memset(dp, 0, sizeof dp); 17 dp[1][1] = 1; 18 dp[k][1] = 1; 19 for(int i = 1; i <= l; ++i) 20 { 21 dp[i + 1][0] += dp[i][1]; 22 dp[i + 1][1] += dp[i][0]; 23 dp[i + k][1] += dp[i][0]; 24 } 25 ll ans = 0; 26 for(int i = 1; i <= l; ++i) ans += dp[i][1]; 27 printf("%lld ", ans); 28 } 29 return 0; 30 }
Problem B Parallel Lines
Solved.
题意:有n个点, 将这些点两两匹配, 求最后平行条数
思路:DFS搜索, 复杂度为$O(15 cdot 13 cdot 11 cdot 9 cdot 7 cdot 5 cdot 3)$
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 const int maxn = 4e3 + 10; 6 7 int gcd(int a,int b) 8 { 9 return b == 0 ? a : gcd(b, a % b); 10 } 11 12 struct node{ 13 int x, y; 14 node(){} 15 node(int x, int y):x(x), y(y){} 16 }arr[maxn]; 17 18 int n; 19 int ans; 20 int vis[maxn]; 21 int G[maxn][maxn]; 22 int X[maxn][maxn], Y[maxn][maxn]; 23 24 void DFS(int cnt, int res) 25 { 26 if(cnt + 2 > n) 27 { 28 ans = max(ans, res); 29 return ; 30 } 31 int pos1 = -1; 32 for(int i = 1; i <= n; ++i) if(!vis[i]) 33 { 34 pos1 = i; 35 vis[i] = 1; 36 break; 37 } 38 for(int i = 1; i <= n; ++i) if(!vis[i]) 39 { 40 int pos2 = i; 41 vis[i] = 1; 42 int tmp = res; 43 int dx = X[pos1][pos2]; 44 int dy = Y[pos1][pos2]; 45 tmp += G[dx][dy]; 46 G[dx][dy]++; 47 DFS(cnt + 2, tmp); 48 G[dx][dy]--; 49 vis[pos2] = 0; 50 } 51 vis[pos1] = 0; 52 } 53 54 55 int main() 56 { 57 while(~scanf("%d", &n)) 58 { 59 memset(vis, 0, sizeof vis); 60 for(int i = 1; i <= n; ++i) scanf("%d %d", &arr[i].x, &arr[i].y); 61 for(int i = 1; i <= n; ++i) 62 { 63 for(int j = i + 1; j <= n; ++j) 64 { 65 int dx = arr[j].x - arr[i].x; 66 int dy = arr[j].y - arr[i].y; 67 int GCD = gcd(dx, dy); 68 dx /= GCD; 69 dy /= GCD; 70 dx += 2000; 71 dy += 2000; 72 X[i][j] = dx; 73 Y[i][j] = dy; 74 } 75 } 76 ans = 0; 77 DFS(0, 0); 78 printf("%d ", ans); 79 } 80 return 0; 81 }
Problem C Medical Checkup
Solved.
题意:有$n$个人去做体检,体检项目无限个,每个人完成一个项目的时间为$h_i$,按顺序做,没轮到就排队 求时刻$t$的时候每个人完成了多少个项目或者正在完成或者正在排队的也要算上
思路:如果$h_i >= h_{i - 1}$ 那么这个人去做,不会有等待时间,否则第一个项目等完后,考虑第二个的时候,需要等待$h_{i - 1} - h_i$的时间,加上自己完成的时间 刚好是$h_{i - 1}$
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define N 100010 6 int n, t, a[N]; 7 8 int main() 9 { 10 while (scanf("%d%d", &n, &t) != EOF) 11 { 12 for (int i = 1; i <= n; ++i) scanf("%d", a + i); 13 ll res = 0; 14 ll tot = 0; 15 for (int i = 1; i <= n; ++i) 16 { 17 tot += a[i]; 18 res = 1; 19 a[i] = max(a[i], a[i - 1]); 20 if (tot <= t + 1) 21 { 22 ll now = t - tot + 1; 23 res += now / a[i] + (now % a[i] != 0); 24 } 25 printf("%lld ", res); 26 } 27 } 28 return 0; 29 }
Problem E Black or White
Solved.
题意:有两个字符串$s$和$t$, 字符只由'B'和'W'组成,操作时选取一段长度不超过$k$的子段,将这一段全染成'B'或者'W',求s -> t 最少操作数
思路:考虑是否跟前面的一起做,转移方程就是$dp[i] = Min_{j + k >= i}(dp[j - 1] + f(j, i))$
$dp[i]$表示处理到第$i$位的最少操作
线段树优化即可。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 500010 5 int n, k; 6 char a[N], b[N]; 7 int f[N]; 8 9 namespace SEG 10 { 11 struct node 12 { 13 int Min[2], lazy[2]; 14 void init() 15 { 16 memset(Min, 0, sizeof Min); 17 memset(lazy, 0, sizeof lazy); 18 } 19 void add(int x, int vis) 20 { 21 Min[vis] += x; 22 lazy[vis] += x; 23 } 24 node operator + (const node &other) const 25 { 26 node res; res.init(); 27 for (int i = 0; i < 2; ++i) 28 res.Min[i] = min(Min[i], other.Min[i]); 29 return res; 30 } 31 }a[N << 2], res; 32 void init() 33 { 34 memset(a, 0, sizeof a); 35 } 36 void pushdown(int id) 37 { 38 for (int i = 0; i < 2; ++i) 39 if (a[id].lazy[i]) 40 { 41 int &x = a[id].lazy[i]; 42 a[id << 1].add(x, i); 43 a[id << 1 | 1].add(x, i); 44 x = 0; 45 } 46 } 47 void update(int id, int l, int r, int ql, int qr, int v, int vis) 48 { 49 if (qr < ql) return; 50 if (l >= ql && r <= qr) 51 { 52 a[id].add(v, vis); 53 return; 54 } 55 int mid = (l + r) >> 1; 56 pushdown(id); 57 if (ql <= mid) update(id << 1, l, mid, ql, qr, v, vis); 58 if (qr > mid) update(id << 1 | 1, mid + 1, r, ql, qr, v, vis); 59 a[id] = a[id << 1] + a[id << 1 | 1]; 60 } 61 void query(int id, int l, int r, int ql, int qr) 62 { 63 if (qr < ql) return; 64 if (l >= ql && r <= qr) 65 { 66 res = res + a[id]; 67 return; 68 } 69 int mid = (l + r) >> 1; 70 pushdown(id); 71 if (ql <= mid) query(id << 1, l, mid, ql, qr); 72 if (qr > mid) query(id << 1 | 1, mid + 1, r, ql, qr); 73 } 74 } 75 // 0 all turn B 76 // 1 all turn W 77 78 int main() 79 { 80 while (scanf("%d%d", &n, &k) != EOF) 81 { 82 scanf("%s", a + 1); 83 scanf("%s", b + 1); 84 SEG::init(); 85 f[1] = (a[1] != b[1]); 86 if (b[1] == 'W') 87 SEG::update(1, 1, n, 1, 1, 1, 0); 88 else 89 SEG::update(1, 1, n, 1, 1, 1, 1); 90 for (int i = 2; i <= n; ++i) 91 { 92 f[i] = f[i - 1] + (a[i] != b[i]); 93 for (int j = 0; j < 2; ++j) 94 SEG::update(1, 1, n, i, i, f[i - 1], j); 95 int last = max(1, i - k + 1); 96 // printf("%d%c", last, " "[i == n]); 97 // 0 98 if (b[i] == 'W') 99 { 100 SEG::update(1, 1, n, i, i, 1, 0); 101 if (b[i - 1] != 'W') 102 SEG::update(1, 1, n, last, i - 1, 1, 0); 103 } 104 // 1 105 if (b[i] == 'B') 106 { 107 SEG::update(1, 1, n, i, i, 1, 1); 108 if (b[i - 1] != 'B') 109 SEG::update(1, 1, n, last, i - 1, 1, 1); 110 } 111 for (int j = 0; j < 2; ++j) 112 SEG::res.Min[j] = (int)1e9; 113 SEG::query(1, 1, n, last, i); 114 for (int j = 0; j < 2; ++j) 115 f[i] = min(f[i], SEG::res.Min[j] + 1); 116 // printf("%d %d %d ", SEG::res.Min[0], SEG::res.Min[1], f[i]); 117 // for (int j = 0; j < 2; ++j) 118 // SEG::res.Min[j] = (int)1e9; 119 // SEG::query(1, 1, n, i, i); 120 // printf("%d %d ", SEG::res.Min[0], SEG::res.Min[1]); 121 } 122 printf("%d ", f[n]); 123 } 124 return 0; 125 }
Problem H Homework
Solved.
题意:有两种作业, 第一种作业有$m$种, 第二种作业有$n-m$种,每个作业有自己的startTime以及endTime, 每天选择一个集合做作业, 求最后能完成的最大值和最小值。
思路:
最大值很显然可以用最大流。
对于最小值, 可以将A类作业和源点相连以及自身对应的时间起点相连, B类作业和汇点相连以及自身对应的时间的终点相连,跑最小割。
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 typedef long long ll; 6 7 const int INF = 0x3f3f3f3f; 8 const int maxn = 1e6 + 10; 9 10 struct Edge{ 11 int to, flow, nxt; 12 Edge(){} 13 Edge(int to, int nxt, int flow):to(to), nxt(nxt), flow(flow){} 14 }edge[maxn << 2]; 15 16 struct node{ 17 int si, ti; 18 node(){} 19 node(int si, int ti):si(si), ti(ti){} 20 }arr[maxn]; 21 22 int head[maxn], dep[maxn]; 23 24 int S, T; 25 int n, m, tot; 26 27 void Init() 28 { 29 memset(head, -1, sizeof head); 30 tot = 0; 31 } 32 33 void addedge(int u,int v, int w, int rw = 0) 34 { 35 edge[tot] = Edge(v, head[u], w); head[u] = tot++; 36 edge[tot] = Edge(u, head[v], rw); head[v] = tot++; 37 } 38 39 bool BFS() 40 { 41 memset(dep, -1, sizeof dep); 42 queue<int>q; 43 q.push(S); 44 dep[S] = 1; 45 while(!q.empty()) 46 { 47 int u = q.front(); 48 q.pop(); 49 for(int i = head[u]; ~i; i = edge[i].nxt) 50 { 51 if(edge[i].flow && dep[edge[i].to] == -1) 52 { 53 dep[edge[i].to] = dep[u] + 1; 54 q.push(edge[i].to); 55 } 56 } 57 } 58 return dep[T] < 0 ? 0 : 1; 59 } 60 61 int DFS(int u,int f) 62 { 63 if(u == T || f == 0) return f; 64 int w, used = 0; 65 for(int i = head[u]; ~i; i = edge[i].nxt) 66 { 67 if(edge[i].flow && dep[edge[i].to] == dep[u] + 1) 68 { 69 w = DFS(edge[i].to, min(f - used, edge[i].flow)); 70 edge[i].flow -= w; 71 edge[i ^ 1].flow += w; 72 used += w; 73 if(used == f) return f; 74 } 75 } 76 if(!used) dep[u] = -1; 77 return used; 78 } 79 80 int Dicnic() 81 { 82 int ans = 0; 83 while(BFS()) 84 { 85 ans += DFS(S, INF); 86 } 87 return ans; 88 } 89 90 int in[maxn], out[maxn], homework[maxn]; 91 92 int main() 93 { 94 while(~scanf("%d %d", &n, &m)) 95 { 96 Init(); 97 for(int i = 1; i <= n; ++i) scanf("%d %d", &arr[i].si, &arr[i].ti); 98 int cnt = 0; 99 S = cnt++; 100 for(int i = 1; i <= 400; ++i) in[i] = cnt++, out[i] = cnt++; 101 for(int i = 1; i <= n; ++i) homework[i] = cnt++; 102 T = cnt++; 103 for(int i = 1; i <= n; ++i) addedge(S, homework[i], 1); 104 for(int i = 1; i <= n; ++i) for(int j = arr[i].si; j <= arr[i].ti; ++j) 105 { 106 addedge(homework[i], in[j], 1); 107 } 108 for(int i = 1; i <= 400; ++i) addedge(in[i], out[i], 1); 109 for(int i = 1; i <= 400; ++i) addedge(out[i], T, 1); 110 int ans1 = Dicnic(); 111 112 Init(); 113 for(int i = 1; i <= m; ++i) 114 { 115 addedge(S, homework[i], 1); 116 for(int j = arr[i].si; j <= arr[i].ti; ++j) 117 { 118 addedge(homework[i], in[j], 1); 119 } 120 } 121 for(int i = 1; i <= 400; ++i) addedge(in[i], out[i], 1); 122 for(int i = m + 1; i <= n; ++i) 123 { 124 addedge(homework[i], T, 1); 125 for(int j = arr[i].si; j <= arr[i].ti; ++j) 126 { 127 addedge(out[j], homework[i], 1); 128 } 129 } 130 int ans2 = Dicnic(); 131 printf("%d %d ", ans1, ans2); 132 } 133 return 0; 134 }
Problem I Starting a Scenic Railroad Service
Solved.
题意:求两种卖票方案需要提供的座位数。
思路:
对于第二种答案, 很显然可以用差分,求最大值得到。
对于第一种答案, 枚举在每个人, 求每个人上车前已经下车以及下车前还没上车的人数, 剩余的人数就是保证这个人上车有座位的座位数, 扫一遍求最大值即可。
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 const int maxn = 2e5 + 10; 6 7 struct node{ 8 int l, r; 9 node(){} 10 node(int l,int r):l(l), r(r){} 11 }arr[maxn]; 12 13 int n; 14 int ans1, ans2; 15 int sum[maxn]; 16 int brr[maxn], crr[maxn]; 17 18 int main() 19 { 20 while(~scanf("%d", &n)) 21 { 22 memset(sum, 0, sizeof sum); 23 ans1 = ans2 = 0; 24 for(int i = 1; i <= n; ++i) 25 { 26 scanf("%d %d", &arr[i].l, &arr[i].r); 27 arr[i].r--; 28 brr[i] = arr[i].r; 29 crr[i] = arr[i].l; 30 sum[arr[i].l]++, sum[arr[i].r + 1]--; 31 } 32 for(int i = 1; i < maxn; ++i) 33 { 34 sum[i] += sum[i - 1]; 35 ans1 = max(ans1, sum[i]); 36 } 37 sort(brr + 1, brr + 1 + n); 38 sort(crr + 1, crr + 1 + n); 39 for(int i = 1; i <= n; ++i) 40 { 41 int L = lower_bound(brr + 1, brr + 1 + n, arr[i].l) - brr; 42 int R = upper_bound(crr + 1, crr + 1 + n, arr[i].r) - crr; 43 R--; 44 int tmp = n - (L - 1) - (n - R); 45 ans2 = max(ans2, tmp); 46 } 47 printf("%d %d ", ans2, ans1); 48 } 49 return 0; 50 }