2017雅礼省选集训做题记录
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2017雅礼省选集训做题记录相关的知识,希望对你有一定的参考价值。
嘛,最近在补雅礼省选前集训的题。都是我会做的题。。那一定是最水的那些题啦
题目在loj.ac上都有。过段时间如果搬了雅礼NOI集训的题应该也会做做的吧。。
Day1
T1
一道经典套路题,做法跟UOJ #228基础数据结构练习题类似。
使用线段树维护。考虑相邻两个数的差值最多变化log次。也就是说,对于每个区间,只要操作二进行大概log次就能使得这个区间内所有数完全一样。所以对于操作二,只要记录一下区间最大最小值,就能直接打标记或者暴力DFS下去。
和UOJ那个题一样,注意一个特殊情况,就是一个区间有两种数,不过大的那个能够被d整除,这时候就可以直接打一个减法标记。不考虑这个情况复杂度会被卡成平方。
1 #include <cstdio> 2 #include <algorithm> 3 4 using namespace std; 5 6 void Get_Val(int &Ret) 7 { 8 Ret = 0; 9 char ch; 10 bool Neg(false); 11 while (ch = getchar(), (ch > ‘9‘ || ch < ‘0‘) && ch != ‘-‘) 12 ; 13 if (ch == ‘-‘) 14 { 15 Neg = true; 16 while (ch = getchar(), ch > ‘9‘ || ch < ‘0‘) 17 ; 18 } 19 do 20 { 21 (Ret *= 10) += ch - ‘0‘; 22 } 23 while (ch = getchar(), ch >= ‘0‘ && ch <= ‘9‘); 24 Ret = (Neg ? -Ret : Ret); 25 } 26 27 const int Max_N(100050); 28 const int INF(2100000000); 29 typedef long long int LL; 30 31 int N, Q, A[Max_N]; 32 33 #define LEFT (segt[cur].l) 34 #define RIGHT (segt[cur].r) 35 #define MID (segt[cur].mid) 36 #define MIN (segt[cur].Min) 37 #define MAX (segt[cur].Max) 38 #define SUM (segt[cur].Sum) 39 #define ADD (segt[cur].Add) 40 #define COV (segt[cur].Cov) 41 #define LCH (cur << 1) 42 #define RCH ((cur << 1) | 1) 43 44 inline int mydiv(const int &n, const int &d) 45 { 46 return n >= 0 ? n / d : n / d - (n % d != 0); 47 } 48 49 struct node 50 { 51 int l, r, mid, Min, Max, Add, Cov; 52 LL Sum; 53 }; 54 55 struct Segment_Tree 56 { 57 node segt[Max_N << 2]; 58 inline void pushup(const int &cur) 59 { 60 MIN = min(segt[LCH].Min, segt[RCH].Min); 61 MAX = max(segt[LCH].Max, segt[RCH].Max); 62 SUM = segt[LCH].Sum + segt[RCH].Sum; 63 } 64 inline void cover(const int &cur, const int &v) 65 { 66 MIN = MAX = v, SUM = (v * 1LL) * (RIGHT - LEFT + 1LL), COV = v, ADD = 0; 67 } 68 inline void plus(const int &cur, const int &v) 69 { 70 MIN += v, MAX += v, SUM += (v * 1LL) * (RIGHT - LEFT + 1LL), ADD += v; 71 } 72 inline void pushdown(const int &cur) 73 { 74 if (COV != INF) 75 cover(LCH, COV), cover(RCH, COV), COV = INF; 76 if (ADD) 77 plus(LCH, ADD), plus(RCH, ADD), ADD = 0; 78 } 79 void build_tree(const int&, const int&, const int&); 80 LL querySum(const int&, const int&, const int&); 81 int queryMin(const int&, const int&, const int&); 82 void Plus(const int&, const int&, const int&, const int&); 83 void Div(const int&, const int&, const int&, const int&); 84 }; 85 Segment_Tree seg; 86 87 void Segment_Tree::build_tree(const int &cur, const int &l, const int &r) 88 { 89 LEFT = l, RIGHT = r, MID = l + ((r - l) >> 1), ADD = 0, COV = INF; 90 if (l == r) 91 { 92 MIN = MAX = SUM = A[l]; 93 return; 94 } 95 build_tree(LCH, l, MID), build_tree(RCH, MID + 1, r), pushup(cur); 96 } 97 98 LL Segment_Tree::querySum(const int &cur, const int &l, const int &r) 99 { 100 if (LEFT == l && RIGHT == r) 101 return SUM; 102 pushdown(cur); 103 if (r <= MID) 104 return querySum(LCH, l, r); 105 else 106 if (l > MID) 107 return querySum(RCH, l, r); 108 else 109 return querySum(LCH, l, MID) + querySum(RCH, MID + 1, r); 110 } 111 112 int Segment_Tree::queryMin(const int &cur, const int &l, const int &r) 113 { 114 if (LEFT == l && RIGHT == r) 115 return MIN; 116 pushdown(cur); 117 if (r <= MID) 118 return queryMin(LCH, l, r); 119 else 120 if (l > MID) 121 return queryMin(RCH, l, r); 122 else 123 return min(queryMin(LCH, l, MID), queryMin(RCH, MID + 1, r)); 124 } 125 126 void Segment_Tree::Plus(const int &cur, const int &l, const int &r, const int &v) 127 { 128 if (LEFT == l && RIGHT == r) 129 { 130 plus(cur, v); 131 return; 132 } 133 pushdown(cur); 134 if (r <= MID) 135 Plus(LCH, l, r, v); 136 else 137 if (l > MID) 138 Plus(RCH, l, r, v); 139 else 140 Plus(LCH, l, MID, v), Plus(RCH, MID + 1, r, v); 141 pushup(cur); 142 } 143 144 void Segment_Tree::Div(const int &cur, const int &l, const int &r, const int &d) 145 { 146 if (LEFT == l && RIGHT == r) 147 { 148 if (mydiv(MIN, d) == mydiv(MAX, d)) 149 cover(cur, mydiv(MIN, d)); 150 else 151 if (MAX - MIN <= 1 && MAX - mydiv(MAX, d) == MIN - mydiv(MIN, d)) 152 plus(cur, mydiv(MAX, d) - MAX); 153 else 154 pushdown(cur), Div(LCH, l, MID, d), Div(RCH, MID + 1, r, d), pushup(cur); 155 return; 156 } 157 pushdown(cur); 158 if (r <= MID) 159 Div(LCH, l, r, d); 160 else 161 if (l > MID) 162 Div(RCH, l, r, d); 163 else 164 Div(LCH, l, MID, d), Div(RCH, MID + 1, r, d); 165 pushup(cur); 166 } 167 168 void init() 169 { 170 Get_Val(N), Get_Val(Q); 171 for (int i = 1;i <= N;++i) 172 Get_Val(A[i]); 173 seg.build_tree(1, 1, N); 174 } 175 176 void work() 177 { 178 int op, l, r, v; 179 while (Q--) 180 { 181 Get_Val(op), Get_Val(l), Get_Val(r), ++l, ++r; 182 if (op == 1) 183 Get_Val(v), seg.Plus(1, l, r, v); 184 if (op == 2) 185 Get_Val(v), seg.Div(1, l, r, v); 186 if (op == 3) 187 printf("%d\n", seg.queryMin(1, l, r)); 188 if (op == 4) 189 printf("%lld\n", seg.querySum(1, l, r)); 190 } 191 } 192 193 int main() 194 { 195 init(); 196 work(); 197 return 0; 198 }
T2
考虑贪心。最优策略一定是,先把第i行任意一个黑色格子那一列提出来,设这个格子为(i,j),然后去把第j行全部染成黑色,最后用第j行去染其它所有行。注意考虑一些奇怪的情况。
不过我的代码好像有锅。。但反正A掉了就没管了。。
1 #include <cstdio> 2 #include <algorithm> 3 4 using namespace std; 5 6 const int Max_N(1050); 7 8 int N; 9 char S[Max_N][Max_N]; 10 int Black[Max_N]; 11 bool AllWhite; 12 13 void init() 14 { 15 AllWhite = true; 16 scanf("%d", &N); 17 for (int i = 1;i <= N;++i) 18 { 19 scanf("%s", S[i] + 1); 20 for (int j = 1;j <= N;++j) 21 AllWhite = (S[i][j] == ‘#‘ ? false: AllWhite), Black[j] += (S[i][j] == ‘#‘); 22 } 23 } 24 25 void work() 26 { 27 int Ans(N << 1); 28 for (int i = 1, Ret = 0;i <= N;++i) 29 { 30 Ret = 1; 31 for (int j = 1;j <= N;++j) 32 if (S[j][i] == ‘#‘) 33 { 34 Ret = 0; 35 break; 36 } 37 for (int j = 1;j <= N;++j) 38 if (S[i][j] == ‘.‘) 39 ++Ret; 40 for (int k = 1;k <= N;++k) 41 if (Black[k] < N) 42 ++Ret; 43 Ans = min(Ans, Ret); 44 } 45 printf("%d", Ans); 46 } 47 48 int main() 49 { 50 init(); 51 if (AllWhite) 52 printf("-1"); 53 else 54 work(); 55 return 0; 56 }
T3
我们首先考虑两种暴力。
第一种暴力是,对于每个询问,直接枚举在a到b之间的i对应的li和ri,然后累加答案。
第二种暴力是,对于每个询问,枚举这个字符串w的O(k^2)个子串,然后计算每个子串在a到b之间有多少个,累加他们对于答案的贡献。
因为我比较菜不会SAM,所以所有计算w的某个子串在某一段字符串中出现次数我是用SA实现的。
注意到Q*K<=2*10^5,这提示我们可以考虑根号分治。我们设定一个阈值为r=sqrt(2*10^5),如果字符串长度大于r,那么询问个数一定小于r。那么复杂度就是O(qsqrt(2*10^5)*logn);如果字符串长度小于r,那么所有的字符串子串之和就是Σr^2<=Σr*sqrt(2*10^5),那么我们的复杂度就是O(Q*Ksqrt(2*10^5)*logn)。
这两个复杂度都是可以接受的。只是实现起来用SA可能要卡常,对于第一种暴力我是用一种奇怪的并查集做法来实现的。
(出题人给的标算用的是SAM)
1 #include <cmath> 2 #include <vector> 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 7 using namespace std; 8 9 void Get_Val(int &Ret) 10 { 11 Ret = 0; 12 char ch; 13 while (ch = getchar(), ch > ‘9‘ || ch < ‘0‘) 14 ; 15 do 16 { 17 (Ret *= 10) += ch - ‘0‘; 18 } 19 while (ch = getchar(), ch >= ‘0‘ && ch <= ‘9‘); 20 } 21 22 const int Max_N(300050); 23 const int Max_M(100050); 24 const int Max_Q(100050); 25 const int Max_K(100050); 26 typedef long long int LL; 27 28 int _N, N, M, Mcut, Q, K, L[Max_M], R[Max_M], St[Max_Q], A[Max_Q], B[Max_Q], S[Max_N], InS[Max_N]; 29 char _S[Max_N]; 30 31 void init() 32 { 33 Get_Val(_N), Get_Val(M), Get_Val(Q), Get_Val(K); 34 scanf("%s", _S + 1); 35 for (int i = 1;i <= M;++i) 36 Get_Val(L[i]), Get_Val(R[i]), ++L[i], ++R[i]; 37 for (int i = 1;i <= _N;++i) 38 S[i] = _S[i]; 39 N = _N; 40 for (int i = 1;i <= Q;++i) 41 { 42 scanf("%s", _S + 1), Get_Val(A[i]), Get_Val(B[i]), ++A[i], ++B[i]; 43 S[++N] = 100000 + i, St[i] = N; 44 for (int j = 1;j <= K;++j) 45 S[++N] = _S[j]; 46 } 47 48 } 49 50 int SA[Max_N], wa[Max_N], wb[Max_N], Cnt[Max_N]; 51 int Rank[Max_N], Height[Max_N], ST[20][Max_N], Log[Max_N]; 52 void getSA() 53 { 54 int M = 200050, *x = wa, *y = wb; 55 memset(Cnt, 0, sizeof(Cnt)); 56 for (int i = 1;i <= N;++i) 57 ++Cnt[x[i] = S[i]]; 58 for (int i = 1;i <= M;++i) 59 Cnt[i] += Cnt[i - 1]; 60 for (int i = N;i >= 1;--i) 61 SA[Cnt[x[i]]--] = i; 62 for (int k = 1, p;k <= N;k <<= 1) 63 { 64 p = 0; 65 for (int i = N - k + 1;i <= N;++i) 66 y[++p] = i; 67 for (int i = 1;i <= N;++i) 68 if (SA[i] > k) 69 y[++p] = SA[i] - k; 70 memset(Cnt, 0, sizeof(Cnt)); 71 for (int i = 1;i <= N;++i) 72 ++Cnt[x[i]]; 73 for (int i = 1;i <= M;++i) 74 Cnt[i] += Cnt[i - 1]; 75 for (int i = N;i >= 1;--i) 76 SA[Cnt[x[y[i]]]--] = y[i]; 77 swap(x, y), x[SA[1]] = (p = 1); 78 for (int i = 2;i <= N;++i) 79 { 80 if (y[SA[i - 1]] != y[SA[i]] || y[SA[i - 1] + k] != y[SA[i] + k]) 81 ++p; 82 x[SA[i]] = p; 83 } 84 if ((M = p) == N) 85 break; 86 } 87 for (int i = 1;i <= N;++i) 88 { 89 Rank[SA[i]] = i; 90 InS[i] = InS[i - 1] + (SA[i] <= _N); 91 } 92 for (int i = 1, k = 0, j;i <= N;++i) 93 { 94 if (k) 95 --k; 96 j = SA[Rank[i] - 1]; 97 while (S[i + k] == S[j + k]) 98 ++k; 99 Height[Rank[i]] = k; 100 } 101 } 102 103 inline int rmq(const int &l, const int &r) 104 { 105 static int rmqk; 106 rmqk = Log[r - l + 1]; 107 return min(ST[rmqk][l], ST[rmqk][r - (1 << rmqk) + 1]); 108 } 109 110 int query(const int &pos, const int &len) 111 { 112 int l, r, mid, up, down; 113 if (pos == 1) 114 up = 1; 115 else 116 { 117 l = 1, r = pos; 118 while (l < r) 119 { 120 mid = l + ((r - l) >> 1); 121 if (rmq(mid + 1, pos) >= len) 122 r = mid; 123 else 124 l = mid + 1; 125 } 126 up = l; 127 } 128 if (pos == N) 129 down = N; 130 else 131 { 132 l = pos + 1, r = N + 1; 133 while (l < r) 134 { 135 mid = l + ((r - l) >> 1); 136 if (rmq(pos + 1, mid) >= len) 137 l = mid + 1; 138 else 139 r = mid; 140 } 141 down = l - 1; 142 } 143 return InS[down] - InS[up - 1]; 144 } 145 146 struct edge 147 { 148 int u, v, w; 149 void give(const int &_u, const int &_v, const int &_w) 150 { 151 u = _u, v = _v, w = _w; 152 } 153 }; 154 edge Edges[Max_N]; 155 int Father[Max_N], lb[Max_N], rb[Max_N]; 156 LL Ans[Max_Q]; 157 158 inline bool operator<(const edge &a, const edge &b) 159 { 160 return a.w > b.w; 161 } 162 163 int Get_Father(const int &x) 164 { 165 return Father[x] == x ? x : Father[x] = Get_Father(Father[x]); 166 } 167 168 struct ask 169 { 170 int pos, len; 171 void give(const int &_pos, const int &_len) 172 { 173 pos = _pos, len = _len; 174 } 175 }; 176 ask Ask[Max_M]; 177 178 inline bool operator<(const ask &a, const ask &b) 179 { 180 return a.len > b.len; 181 } 182 183 void work1() 184 { 185 for (int i = 1;i <= N;++i) 186 Father[i] = i, lb[i] = rb[i] = i; 187 for (int i = 2;i <= N;++i) 188 Edges[i - 1].give(i - 1, i, Height[i]); 189 for (int i = 1;i <= M;++i) 190 Ask[i].give(i, R[i] - L[i] + 1); 191 sort(Edges + 1, Edges + 1 + (N - 1)); 192 sort(Ask + 1, Ask + 1 + M); 193 for (register int i = 1, j = 1, u, v, askp;i <= M;++i) 194 { 195 while (j <= N - 1 && Edges[j].w >= Ask[i].len) 196 { 197 if ((u = Get_Father(Edges[j].u)) != (v = Get_Father(Edges[j].v))) 198 Father[v] = u, lb[u] = min(lb[u], lb[v]), rb[u] = max(rb[u], rb[v]); 199 ++j; 200 } 201 askp = Ask[i].pos; 202 for (int q = 1, pos;q <= Q;++q) 203 if (A[q] <= askp && askp <= B[q]) 204 { 205 pos = Get_Father(Rank[St[q] + L[askp]]); 206 Ans[q] += InS[rb[pos]] - InS[lb[pos] - 1]; 207 } 208 } 209 for (int i = 1;i <= Q;++i) 210 printf("%lld\n", Ans[i]); 211 } 212 213 vector<int> PP[Max_M]; 214 215 int query(const vector<int> &Pos, const int &up, const int &down) 216 { 217 if (!Pos.size()) 218 return 0; 219 if (Pos[Pos.size() - 1] < up) 220 return 0; 221 if (Pos[0] > down) 222 return 0; 223 int ll, rr, l, r, mid; 224 l = 0, r = Pos.size() - 1; 225 while (l < r) 226 { 227 mid = l + ((r - l) >> 1); 228 if (Pos[mid] >= up) 229 r = mid; 230 else 231 l = mid + 1; 232 } 233 ll = l; 234 l = 0, r = Pos.size(); 235 while (l < r) 236 { 237 mid = l + ((r - l) >> 1); 238 if (Pos[mid] <= down) 239 l = mid + 1; 240 else 241 r = mid; 242 } 243 rr = l - 1; 244 return ll <= rr ? rr - ll + 1 : 0; 245 } 246 247 void work2() 248 { 249 for (int i = 1;i <= N;++i) 250 ST[0][i] = Height[i]; 251 for (int j = 1;(1 << j) <= N;++j) 252 for (int i = 1;i + (1 << j) - 1 <= N;++i) 253 ST[j][i] = min(ST[j - 1][i], ST[j - 1][i + (1 << (j - 1))]); 254 Log[0] = Log[1] = 0; 255 for (int i = 2;i <= N;++i) 256 Log[i] = Log[i >> 1] + 1; 257 LL Ans, qwq; 258 for (int i = 1;i <= M;++i) 259 PP[(L[i] - 1) * K + R[i]].push_back(i); 260 for (int i = 1;i <= Q;++i) 261 { 262 Ans = 0LL; 263 for (int a = 1;a <= K;++a) 264 for (int b = a;b <= K;++b) 265 if (qwq = query(PP[(a - 1) * K + b], A[i], B[i])) 266 Ans += (query(Rank[St[i] + a], b - a + 1) * 1LL) * (qwq * 1LL); 267 printf("%lld\n", Ans); 268 } 269 } 270 271 int main() 272 { 273 init(); 274 getSA(); 275 Mcut = static_cast<int>(sqrt(M * 1.0)); 276 if (K >= Mcut) 277 work1(); 278 else 279 work2(); 280 return 0; 281 }
Day2
T1
这个题我一开始不大会,看了solution也没有看懂出题人给的做法。后来去膜了一发kry的做法。
设F[i][j]表示dp到第i个格子,第i个格子的水位高度为j可以满足的最多条件。设第i个格子左边的挡板高度为H[i],那么当j<=H[i]的时候,它可以由所有0<=k<=H[i]的F[i][k]转移过来,具体而言就是max{F[i][k],0<=k<=H[i]};当j>H[i]的时候,它可以由F[i-1][j]转移过来。我们用线段树维护第二维,前一种情况是区间覆盖,后一种情况是区间加法。然后分O(m)段考虑所有为1的条件,至于为0的条件可以最后加上去。
1 #include <map> 2 #include <cstdio> 3 #include <vector> 4 #include <algorithm> 5 6 using namespace std; 7 8 void Get_Val(int &Ret) 9 { 10 Ret = 0; 11 char ch; 12 while (ch = getchar(), ch > ‘9‘ || ch < ‘0‘) 13 ; 14 do 15 { 16 (Ret *= 10) += ch - ‘0‘; 17 } 18 while (ch = getchar(), ch >= ‘0‘ && ch <= ‘9‘); 19 } 20 21 const int Max_N(100050); 22 const int Max_M(300050); 23 24 int N, M, H[Max_N]; 25 vector<int> V0[Max_N], V1[Max_N]; 26 int Tot, P[Max_M]; 27 28 void clear() 29 { 30 Tot = 0; 31 for (int i = 1;i <= N;++i) 32 V0[i].clear(), V1[i].clear(); 33 } 34 35 void init() 36 { 37 Get_Val(N), Get_Val(M); 38 for (int i = 2;i <= N;++i) 39 Get_Val(H[i]); 40 int x, y, k; 41 while (M--) 42 { 43 Get_Val(x), Get_Val(y), Get_Val(k); 44 if (k) 45 V1[x].push_back(y); 46 else 47 V0[x].push_back(y); 48 } 49 } 50 51 void lisanhua() 52 { 53 H[1] = 0; 54 for (int i = 1;i <= N;++i) 55 { 56 P[++Tot] = H[i]; 57 for (int j = 0;j != V0[i].size();++j) 58 P[++Tot] = V0[i][j], P[++Tot] = V0[i][j] + 1; 59 for (int j = 0;j != V1[i].size();++j) 60 P[++Tot] = V1[i][j], P[++Tot] = V1[i][j] + 1; 61 } 62 sort(P + 1, P + 1 + Tot); 63 map<int, int> S; 64 S[P[1]] = (M = 0); 65 for (int i = 2;i <= Tot;++i) 66 { 67 if (P[i] != P[i - 1]) 68 ++M; 69 S[P[i]] = M; 70 } 71 H[1] = ++M; 72 for (int i = 2;i <= N;++i) 73 H[i] = S[H[i]]; 74 for (int i = 1;i <= N;++i) 75 { 76 sort(V1[i].begin(), V1[i].end()); 77 for (int j = 0;j != V0[i].size();++j) 78 V0[i][j] = S[V0[i][j]]; 79 for (int j = 0;j != V1[i].size();++j) 80 V1[i][j] = S[V1[i][j]]; 81 } 82 } 83 84 #define LEFT (segt[cur].l) 85 #define RIGHT (segt[cur].r) 86 #define MID (segt[cur].mid) 87 #define MAX (segt[cur].Max) 88 #define TAG1 (segt[cur].Tag1) 89 #define TAG2 (segt[cur].Tag2) 90 #define LCH (cur << 1) 91 #define RCH ((cur << 1) | 1) 92 93 struct node 94 { 95 int l, r, mid; 96 int Max, Tag1, Tag2; 97 }; 98 99 struct Segment_Tree 100 { 101 node segt[Max_M << 2]; 102 inline void pushup(const int &cur) 103 { 104 MAX = max(segt[LCH].Max, segt[RCH].Max); 105 } 106 inline void Cov(const int &cur, const int &v) 107 { 108 MAX = v, TAG1 = v, TAG2 = 0; 109 } 110 inline void Add(const int &cur, const int &v) 111 { 112 MAX += v, TAG2 += v; 113 } 114 inline void pushdown(const int &cur) 115 { 116 if (TAG1 != -1) 117 Cov(LCH, TAG1), Cov(RCH, TAG1), TAG1 = -1; 118 if (TAG2) 119 Add(LCH, TAG2), Add(RCH, TAG2), TAG2 = 0; 120 } 121 void build_tree(const int&, const int&, const int&); 122 void Cover(const int&, const int&, const int&, const int&); 123 void Plus(const int&, const int&, const int&, const int&); 124 int query(const int&, const int&, const int&); 125 }; 126 Segment_Tree seg; 127 128 void Segment_Tree::build_tree(const int &cur, const int &l, const int &r) 129 { 130 LEFT = l, RIGHT = r, MID = l + ((r - l) >> 1), MAX = 0, TAG1 = -1, TAG2 = 0; 131 if (l == r) 132 return; 133 build_tree(LCH, l, MID), build_tree(RCH, MID + 1, r); 134 } 135 136 void Segment_Tree::Cover(const int &cur, const int &l, const int &r, const int &v) 137 { 138 if (LEFT == l && RIGHT == r) 139 { 140 Cov(cur, v); 141 return; 142 } 143 pushdown(cur); 144 if (r <= MID) 145 Cover(LCH, l, r, v); 146 else 147 if (l > MID) 148 Cover(RCH, l, r, v); 149 else 150 Cover(LCH, l, MID, v), Cover(RCH, MID + 1, r, v); 151 pushup(cur); 152 } 153 154 void Segment_Tree::Plus(const int &cur, const int &l, const int &r, const int &v) 155 { 156 if (LEFT == l && RIGHT == r) 157 { 158 Add(cur, v); 159 return; 160 } 161 pushdown(cur); 162 if (r <= MID) 163 Plus(LCH, l, r, v); 164 else 165 if (l > MID) 166 Plus(RCH, l, r, v); 167 else 168 Plus(LCH, l, MID, v), Plus(RCH, MID + 1, r, v); 169 pushup(cur); 170 } 171 172 int Segment_Tree::query(const int &cur, const int &l, const int &r) 173 { 174 if (LEFT == l && RIGHT == r) 175 return MAX; 176 pushdown(cur); 177 if (r <= MID) 178 return query(LCH, l, r); 179 else 180 if (l > MID) 181 return query(RCH, l, r); 182 else 183 return max(query(LCH, l, MID), query(RCH, MID + 1, r)); 184 } 185 186 void dp() 187 { 188 seg.build_tree(1, 0, M); 189 for (int i = 1, QAQ;i <= N;++i) 190 { 191 QAQ = seg.query(1, 0, H[i]); 192 seg.Cover(1, 0, H[i], QAQ); 193 if (V1[i].size()) 194 { 195 if (V1[i][V1[i].size() - 1] < H[i]) 196 { 197 for (int j = V1[i].size() - 1;j >= 0;--j) 198 if (j == V1[i].size() - 1) 199 seg.Cover(1, V1[i][j] + 1, H[i], QAQ + (j + 1)); 200 else 201 if (V1[i][j + 1] != V1[i][j]) 202 seg.Cover(1, V1[i][j] + 1, V1[i][j + 1], QAQ + (j + 1)); 203 if (H[i] + 1 <= M) 204 seg.Plus(1, H[i] + 1, M, V1[i].size()); 205 } 206 else 207 for (int t = 0;t != V1[i].size();++t) 208 if (V1[i][t] >= H[i]) 209 { 210 for (int j = t - 1;j >= 0;--j) 211 if (j == t - 1) 212 seg.Cover(1, V1[i][j] + 1, H[i], QAQ + (j + 1)); 213 else 214 if (V1[i][j + 1] != V1[i][j]) 215 seg.Cover(1, V1[i][j] + 1, V1[i][j + 1], QAQ + (j + 1)); 216 if (H[i] + 1 <= M) 217 seg.Plus(1, H[i] + 1, M, t); 218 for (int j = t;j != V1[i].size();++j) 219 seg.Plus(1, V1[i][j] + 1, M, +1); 220 break; 221 } 222 } 223 for (int j = 0;j != V0[i].size();++j) 224 seg.Plus(1, 0, V0[i][j], +1); 225 } 226 printf("%d\n", seg.segt[1].Max); 227 } 228 229 int main() 230 { 231 int T; 232 Get_Val(T); 233 while (T--) 234 { 235 clear(); 236 init(); 237 lisanhua(); 238 dp(); 239 } 240 return 0; 241 }
T2
一个经典问题,具体做法见NOI2011 兔兔与蛋蛋。需要注意的trick一个是这个博弈问题的经典结论,还有就是如何判断一个点或者一条边是否一定在二分图最大匹配上:从未匹配点开始用DFS或者BFS找交错轨不断标记即可。
1 #include <queue> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 6 using namespace std; 7 8 void Get_Val(int &Ret) 9 { 10 Ret = 0; 11 char ch; 12 while (ch = getchar(), ch > ‘9‘ || ch < ‘0‘) 13 ; 14 do 15 { 16 (Ret *= 10) += ch - ‘0‘; 17 } 18 while (ch = getchar(), ch >= ‘0‘ && ch <= ‘9‘); 19 } 20 21 const int Max_NM(105); 22 const int Max_V(105 * 105); 23 const int Max_E((105 * 105 + 105 * 105 * 4) * 2); 24 const int INF(0X3F3F3F3F); 25 26 bool left[Max_V], can[Max_V], done[2][Max_V]; 27 28 struct Graph 29 { 30 void start(const int &v, const int &s, const int &t) 31 { 32 V = v, S = s, T = t, Total = 0; 33 memset(Head, 0, sizeof(Head)); 34 memset(To, 0, sizeof(To)), memset(Next, 0, sizeof(Next)); 35 memset(Cap, 0, sizeof(Cap)), memset(Flow, 0, sizeof(Flow)); 36 } 37 int V, S, T; 38 int Head[Max_V], Total, To[Max_E], Next[Max_E], Cap[Max_E], Flow[Max_E]; 39 int Dist[Max_V], Cur[Max_V]; 40 inline void Add_Edge(const int &tot, const int &s, const int &t, const int &c) 41 { 42 To[tot] = t, Next[tot] = Head[s], Head[s] = tot, Cap[tot] = c, Flow[tot] = 0; 43 } 44 inline void Add_Link(const int &s, const int &t, const int &c) 45 { 46 Total += 2, Add_Edge(Total, s, t, c), Add_Edge(Total ^ 1, t, s, 0); 47 } 48 bool BFS() 49 { 50 queue<int> Q; 51 memset(Dist, 0, sizeof(Dist)), Q.push(S), Dist[S] = 1; 52 int u, v; 53 while (Q.size()) 54 { 55 u = Q.front(), Q.pop(); 56 for (int i = Head[u];i;i = Next[i]) 57 if (Cap[i] > Flow[i] && !Dist[v = To[i]]) 58 Dist[v] = Dist[u] + 1, Q.push(v); 59 } 60 return Dist[T]; 61 } 62 int DFS(const int&, int); 63 int Dinic() 64 { 65 int Ans(0); 66 while (BFS()) 67 { 68 for (int i = 1;i <= V;++i) 69 Cur[i] = Head[i]; 70 Ans += DFS(S, INF); 71 } 72 return Ans; 73 } 74 void bianli(const int&, const int&); 75 }; 76 Graph G; 77 78 void Graph::bianli(const int &u, const int &c) 79 { 80 done[c][u] = true; 81 if (left[u] == c) 82 can[u] = true; 83 for (int i = Head[u], v;i;i = Next[i]) 84 if (!done[c][v = To[i]] && ((Cap[i] == Flow[i]) ^ c)) 85 bianli(v, c); 86 } 87 88 int Graph::DFS(const int &u, int a) 89 { 90 if (u == T || a == 0) 91 return a; 92 int f, Ans(0), v; 93 for (int &i = Cur[u];i;i = Next[i]) 94 if (Dist[v = To[i]] == Dist[u] + 1 && (f = DFS(v, min(a, Cap[i] - Flow[i]))) > 0) 95 { 96 Ans += f, Flow[i] += f, Flow[i ^ 1] -= f; 97 if ((a -= f) == 0) 98 break; 99 } 100 return Ans; 101 } 102 103 int N, M; 104 char S[Max_NM][Max_NM]; 105 106 inline bool test(const int &x, const int &y) 107 { 108 return x >= 1 && x <= N && y >= 1 && y <= M; 109 } 110 111 inline int getnumber(const int &x, const int &y) 112 { 113 return (x - 1) * M + y; 114 } 115 116 void init() 117 { 118 scanf("%d%d", &N, &M); 119 for (int i = 1;i <= N;++i) 120 scanf("%s", S[i] + 1); 121 } 122 123 void makegraph() 124 { 125 const int dx[] = {+1, -1, +0, +0}; 126 const int dy[] = {+0, +0, +1, -1}; 127 G.start(N * M + 2, N * M + 1, N * M + 2); 128 for (int i = 1;i <= N;++i) 129 for (int j = 1;j <= M;++j) 130 if (S[i][j] == ‘.‘) 131 if ((i + j) & 1) 132 { 133 G.Add_Link(G.S, getnumber(i, j), 1); 134 left[getnumber(i, j)] = true; 135 for (int k = 0, x, y;k != 4;++k) 136 if (test(x = i + dx[k], y = j + dy[k]) && S[x][y] == ‘.‘) 137 G.Add_Link(getnumber(i, j), getnumber(x, y), 1); 138 } 139 else 140 G.Add_Link(getnumber(i, j), G.T, 1); 141 } 142 143 void work() 144 { 145 G.Dinic(); 146 G.bianli(G.S, 1), G.bianli(G.T, 0); 147 int cnt(0); 148 for (int i = 1;i <= N * M;++i) 149 cnt += can[i]; 150 printf("%d\n", cnt); 151 for (int i = 1;i <= N;++i) 152 for (int j = 1;j <= M;++j) 153 if (can[getnumber(i, j)]) 154 printf("%d %d\n", i, j); 155 } 156 157 int main() 158 { 159 init(); 160 makegraph(); 161 work(); 162 return 0; 163 }
T3
李超线段树裸题。一个区间有两条线段就把“优势区间”较小的那一条线段往对应的一侧不断下传即可。
1 #include <cstdio> 2 #include <algorithm> 3 4 using namespace std; 5 6 const int Max_X(100050); 7 8 void Get_Val(int &Ret) 9 { 10 Ret = 0; 11 char ch; 12 bool Neg(false); 13 while (ch = getchar(), (ch > ‘9‘ || ch < ‘0‘) && ch != ‘-‘) 14 ; 15 if (ch == ‘-‘) 16 { 17 Neg = true; 18 while (ch = getchar(), ch > ‘9‘ || ch < ‘0‘) 19 ; 20 } 21 do 22 { 23 (Ret *= 10) += ch - ‘0‘; 24 } 25 while (ch = getchar(), ch >= ‘0‘ && ch <= ‘9‘); 26 Ret = (Neg ? -Ret : Ret); 27 } 28 29 struct segment 30 { 31 segment(const int &_x1 = 0, const int &_y1 = 0, const int &_x2 = 0, const int &_y2 = 0) : 32 x1(_x1), y1(_y1), x2(_x2), y2(_y2) 33 { 34 if (x1 != x2) 35 { 36 k = (y2 - y1 + 0.0) / (x2 - x1 + 0.0); 37 b = y1 - k * x1; 38 } 39 else 40 k = 0.0, b = -1E13; 41 } 42 int x1, y1, x2, y2; 43 double k, b; 44 double give(const int &x) const 45 { 46 if (x == x1) 47 return y1 * 1.0; 48 else 49 if (x == x2) 50 return y2 * 1.0; 51 else 52 return k * (x * 1.0) + b; 53 } 54 }; 55 56 #define LEFT (segt[cur].l) 57 #define RIGHT (segt[cur].r) 58 #define MID (segt[cur].mid) 59 #define VAL (segt[cur].val) 60 #define LCH (cur << 1) 61 #define RCH ((cur << 1) | 1) 62 63 struct node 64 { 65 int l, r, mid; 66 segment val; 67 }; 68 69 struct Segment_Tree 70 { 71 node segt[Max_X << 2]; 72 void build_tree(const int&, const int&, const int&); 73 void cover(const int&, const segment&); 74 void insert(const int&, const int&, const int&, const segment&); 75 double query(const int&, const int&); 76 }; 77 Segment_Tree seg; 78 79 void Segment_Tree::build_tree(const int &cur, const int &l, const int &r) 80 { 81 LEFT = l, RIGHT = r, MID = l + ((r - l) >> 1); 82 if (l == r) 83 return; 84 build_tree(LCH, l, MID), build_tree(RCH, MID + 1, r); 85 } 86 87 void Segment_Tree::insert(const int &cur, const int &l, const int &r, const segment &val) 88 { 89 if (LEFT == l && RIGHT == r) 90 { 91 cover(cur, val); 92 return; 93 } 94 if (r <= MID) 95 insert(LCH, l, r, val); 96 else 97 if (l > MID) 98 insert(RCH, l, r, val); 99 else 100 insert(LCH, l, MID, val), insert(RCH, MID + 1, r, val); 101 } 102 103 void Segment_Tree::cover(const int &cur, const segment &val) 104 { 105 if (LEFT == RIGHT) 106 { 107 if (val.give(LEFT) > VAL.give(LEFT)) 108 VAL = val; 109 return; 110 } 111 double VAL_l(VAL.give(LEFT)), VAL_r(VAL.give(RIGHT)); 112 double val_l(val.give(LEFT)), val_r(val.give(RIGHT)); 113 if (val_l <= VAL_l && val_r <= VAL_r) 114 return; 115 if (VAL_l <= val_l && VAL_r <= val_r) 116 { 117 VAL = val; 118 return; 119 } 120 double VAL_m(VAL.give(MID + 1)), val_m(val.give(MID + 1)); 121 if (val_l <= VAL_l) 122 if (val_m <= VAL_m) 123 cover(RCH, val); 124 else 125 cover(LCH, VAL), VAL = val; 126 else 127 if (VAL_m <= val_m) 128 cover(RCH, VAL), VAL = val; 129 else 130 cover(LCH, val); 131 } 132 133 double Segment_Tree::query(const int &cur, const int &x) 134 { 135 if (LEFT == RIGHT) 136 return VAL.give(x); 137 if (x <= MID) 138 return max(VAL.give(x), query(LCH, x)); 139 else 140 return max(VAL.give(x), query(RCH, x)); 141 } 142 143 int BIT[Max_X]; 144 145 inline int lowbit(const int &x) 146 { 147 return x & -x; 148 } 149 150 void insert(int i, const int &v) 151 { 152 while (i <= 100000) 153 BIT[i] += v, i += lowbit(i); 154 } 155 156 int query(int i) 157 { 158 int Ret(0); 159 while (i) 160 Ret += BIT[i], i -= lowbit(i); 161 return Ret; 162 } 163 164 int N, M; 165 double Record[Max_X]; 166 167 void insert(int x1, int y1, int x2, int y2) 168 { 169 if (x1 > x2) 170 swap(x1, x2), swap(y1, y2); 171 if (x1 == x2) 172 { 173 if (x1 >= 1 && x1 <= 100000) 174 { 175 Record[x1] = max(Record[x2], max(y1 * 1.0, y2 * 1.0)); 176 insert(x1, +1), insert(x1 + 1, -1); 177 } 178 return; 179 } 180 int l, r; 181 l = max(1, x1), r = min(100000, x2); 182 if (l <= r) 183 seg.insert(1, l, r, segment(x1, y1, x2, y2)), insert(l, +1), insert(r + 1, -1); 184 } 185 186 void init() 187 { 188 Get_Val(N), Get_Val(M); 189 seg.build_tree(1, 1, 100000); 190 for (int x = 1;x <= 100000;++x) 191 Record[x] = -1E13; 192 int x1, y1, x2, y2; 193 while (N--) 194 { 195 Get_Val(x1), Get_Val(y1), Get_Val(x2), Get_Val(y2); 196 insert(x1, y1, x2, y2); 197 } 198 } 199 200 void work() 201 { 202 int op, x0, x1, y1, x2, y2; 203 while (M--) 204 { 205 Get_Val(op); 206 if (op == 0) 207 { 208 Get_Val(x1), Get_Val(y1), Get_Val(x2), Get_Val(y2); 209 insert(x1, y1, x2, y2); 210 } 211 else 212 { 213 Get_Val(x0); 214 if (query(x0)) 215 printf("%lf\n", max(Record[x0], seg.query(1, x0))); 216 else 217 printf("0\n"); 218 } 219 } 220 } 221 222 int main() 223 { 224 init(); 225 work(); 226 return 0; 227 }
以上是关于2017雅礼省选集训做题记录的主要内容,如果未能解决你的问题,请参考以下文章