*注意:这套题目应版权方要求,不得公示题面。
因为业务水平下滑太严重,去和高一考NOIP模拟,sad。。。
Problem A 妹子
题目大意
给定两个矩形,问一个能否将一个矩形放在另一个矩形里面。
先可以根据面积判断哪一个被放在里面,然后判断一下能不能直接放或者旋转90°放进去。
如果不行的话,接着考虑旋转。我们考虑这样↓放置小矩形。
连接$EG$,作$GI\perp AD$于点$I$,$FJ\perp AD$与点$D$,$GK\perp JF$于点$G$。
考虑上下界$AB$和$CD$和左界$AD$的限制,暂时放开$BC$的限制,然后我们考虑最小化$GI$。
因为$GI^{2} = GE^2 - IE^2$,$GE$是定值,所以最小化$IG$等价于最大化$IE$。
易证四边形$JKGI$是矩形,所以$JI = KG$。易证$\angle FGK=\angle JFE=\angle DEH $。
再由$EH = FG,\angle EDH = \angle FKG = 90^{\circ}$,可得$\triangle EDH \cong \triangle GKF$。
所以$JI = KG = ED$当$E$点向下移动的时候$DI$增大,$JI,ED$减小,所以当$DJ = AD$时,$IG$有最小值。
为了满足IG最小,我们这样放置矩形↓
设$AF = x, \frac{EF}{EH} = k, w_{1} = EF, w_{2} = AD$。
因为$\left\{\begin{matrix}\angle AFE = \angle DEH\ \ \ \ \ \ \ \ \ \ \\\angle EAF = \angle EDH = 90^{\circ} \end{matrix}\right.$,所以$\triangle AFE \sim \triangle DEH$、
所以$ED = \frac{x}{k}, AE = \sqrt{w_{1}^{2} - x^{2}}$,然后有$k \cdot w_{2} - x = k\sqrt{w_{1}^{2} - x^{2}}$。
然后有:
$\left\{ \begin{matrix} k^{2} \cdot w_{2}^2 - 2x\cdot k\cdot w_{2} + x^2 = k^2w_{1}^{2} - k^2x^{2} \\ 0< x \leqslant w_{1}\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \\ k \cdot w_{2} - x > 0\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \end{matrix} \right.$
解一下,判断是否满足$CD$限制就行了。1
Code
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <cmath> 5 using namespace std; 6 typedef bool boolean; 7 8 int T; 9 int w1, h1, w2, h2; 10 double w; 11 12 inline void init() { 13 scanf("%d%d%d%d", &w1, &h1, &w2, &h2); 14 if (w1 * h1 > w2 * h2) { 15 swap(w1, w2); 16 swap(h1, h2); 17 } 18 } 19 20 boolean checkRoot(double x) { 21 if (x < 0 || x > w1) 22 return false; 23 double k = w1 * 1.0 / h1; 24 double y = sqrt(w1 * w1 - x * x); 25 return x + y / k <= h2; 26 } 27 28 boolean check() { 29 double k = w1 * 1.0 / h1; 30 double a = - k * k - 1; 31 double b = 2 * k * w2; 32 double c = k * (w1 * w1 - w2 * w2); 33 double delta = b * b - 4 * a * c; 34 if (delta < 0) 35 return false; 36 double qd = sqrt(delta); 37 double x1 = (-b - qd) / (2 * a), x2 = (-b + qd) / (2 * a); 38 return checkRoot(x1) || checkRoot(x2); 39 } 40 41 namespace old { 42 43 boolean check() { 44 if (w1 <= w2 && h1 <= h2) 45 return true; 46 if (w1 >= w2 && h1 >= h2) 47 return true; 48 return false; 49 } 50 51 inline boolean solve() { 52 if (check()) { 53 puts("Yes"); 54 return true; 55 } 56 57 swap(h1, w1); 58 if (check()) { 59 puts("Yes"); 60 return true; 61 } 62 63 swap(h1, w1); 64 swap(h2, w2); 65 if (check()) { 66 puts("Yes"); 67 return true; 68 } 69 return false; 70 } 71 72 } 73 74 inline void solve() { 75 if (old::solve()) 76 return; 77 if (check()) 78 puts("Yes"); 79 else { 80 swap(h1, w1); 81 if (check()) 82 puts("Yes"); 83 else 84 puts("No"); 85 } 86 } 87 88 int main() { 89 scanf("%d", &T); 90 while (T--) { 91 init(); 92 solve(); 93 } 94 return 0; 95 }
Problem B 旅程
题目大意
给定$n$个点的带权有向完全图。要求支持删除一条边以及询问两点之间的最短路。
$1\leqslant n\leqslant 200, 1\leqslant m\leqslant 10^5$,删除操作不超过$200$次。
没有插入,倒着做,变成加。
然后考虑这条边带来的贡献,枚举一对点,用它来更新最短路。
时间复杂度$O(n^3 + m)$
Code
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstring> 4 #include <cstdio> 5 using namespace std; 6 typedef bool boolean; 7 8 const int N = 205; 9 const signed int inf = (signed) (~0u >> 2); 10 11 typedef class Operation { 12 public: 13 int type; 14 int x, y; 15 int data; 16 17 Operation() { } 18 Operation(int type, int x, int y):type(type), x(x), y(y) { } 19 }Operation; 20 21 int n, m; 22 int d[N][N]; 23 int f[N][N]; 24 Operation *os; 25 26 inline void init() { 27 scanf("%d%d", &n, &m); 28 for (int i = 1; i <= n; i++) 29 for (int j = 1; j <= n; j++) 30 scanf("%d", d[i] + j); 31 os = new Operation[(m + 1)]; 32 for (int i = 1, op, x, y; i <= m; i++) { 33 scanf("%d%d%d", &op, &x, &y); 34 os[i] = Operation(op, x, y); 35 if (op == 1) { 36 os[i].data = d[x][y]; 37 d[x][y] = inf; 38 } 39 } 40 } 41 42 inline void solve() { 43 memcpy(f, d, sizeof(f)); 44 for (int i = 1; i <= n; i++) 45 f[i][i] = 0; 46 for (int k = 1; k <= n; k++) 47 for (int i = 1; i <= n; i++) 48 if (i ^ k) 49 for (int j = 1; j <= n; j++) 50 if ((j ^ k) && (j ^ i)) 51 f[i][j] = min(f[i][k] + f[k][j], f[i][j]); 52 53 for (int i = m; i; i--) { 54 int op = os[i].type, x = os[i].x, y = os[i].y; 55 if (op == 1) { 56 int data = os[i].data; 57 if (data != inf) { 58 for (int u = 1; u <= n; u++) 59 for (int v = 1; v <= n; v++) 60 if (u ^ v) 61 f[u][v] = min(f[u][x] + data + f[y][v], f[u][v]); 62 } 63 } else { 64 if (f[x][y] == inf) 65 os[i].data = -1; 66 else 67 os[i].data = f[x][y]; 68 } 69 } 70 71 for (int i = 1; i <= m; i++) 72 if (os[i].type == 2) 73 printf("%d\n", os[i].data); 74 } 75 76 int main() { 77 // freopen("journey.in", "r", stdin); 78 // freopen("journey.out", "w", stdout); 79 init(); 80 solve(); 81 return 0; 82 }
Problem C 老大
题目大意
要求在树上找两个点,然后其他所有点到它们两个点的距离的最小值的最大值最小。
Solution 1 Binary search
二分答案$mid$,从最后一个点向上跳$mid$个点作为第一个选定点,然后把它的势力范围内的点都删掉,然后对剩下的点做一次,再判断是否有点剩余。
时间复杂度$O(n\log n)$
Solution 2 Dynamic programming
考虑只选一个点的时候,答案是最长链长度除以二向上取整。(根据最长链定义然后用反证法)。
选两个点就相当于每个点有个势力范围,分界边将树分成一个比较好看的子树,以及它的补。正反各一次求树上最长链dp就完了。
Code
1 #include <algorithm> 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstdio> 5 #include <vector> 6 #include <ctime> 7 using namespace std; 8 typedef bool boolean; 9 10 typedef class Data { 11 public: 12 int mxlen; 13 int mxdep; 14 15 Data():mxlen(0), mxdep(0) { }; 16 Data(int mxlen, int mxdep):mxlen(mxlen), mxdep(mxdep) { } 17 18 Data operator + (Data b) { 19 Data rt; 20 rt.mxlen = max(mxlen, max(b.mxlen, b.mxdep + mxdep)); 21 rt.mxdep = max(mxdep, b.mxdep); 22 return rt; 23 } 24 25 Data trans() { 26 return Data(mxlen, mxdep + 1); 27 } 28 }Data; 29 30 int n, res = 2333333; 31 vector<int> *g; 32 vector<int> *ts; 33 Data *fu, *fd; 34 35 Data *top; 36 Data *pool; 37 38 Data* alloc(int size) { 39 Data* rt = top; 40 top = top + size; 41 return rt; 42 } 43 44 inline void init() { 45 scanf("%d", &n); 46 fu = new Data[(n + 1)]; 47 fd = new Data[(n + 1)]; 48 pool = new Data[(n << 2) + 10000]; 49 g = new vector<int>[(n + 1)]; 50 ts = new vector<int>[(n + 1)]; 51 top = pool; 52 for (int i = 1, u, v; i < n; i++) { 53 scanf("%d%d", &u, &v); 54 g[u].push_back(v); 55 g[v].push_back(u); 56 } 57 // cerr << clock() << endl; 58 } 59 60 void dp1(int p, int fa) { 61 Data& f = fu[p]; 62 for (int i = 0; i < (signed) g[p].size(); i++) { 63 int e = g[p][i]; 64 if (e == fa) 65 continue; 66 ts[p].push_back(e); 67 dp1(e, p); 68 f = f + fu[e].trans(); 69 } 70 } 71 72 void dp2(int p) { 73 Data f = fd[p].trans() + Data(0, 0); 74 if (p == 1) 75 f = Data(0, 0); 76 int s = (signed) ts[p].size(); 77 Data* pred = alloc(s + 1); 78 Data* sufd = alloc(s + 1); 79 Data d(0, 0); 80 for (int i = 0; i < s; i++) { 81 int e = ts[p][i]; 82 d = d + fu[e].trans(); 83 pred[i] = d; 84 } 85 d = Data(0, 0); 86 for (int i = s - 1; ~i; i--) { 87 int e = ts[p][i]; 88 d = d + fu[e].trans(); 89 sufd[i] = d; 90 } 91 92 for (int i = 0; i < s; i++) { 93 int e = ts[p][i]; 94 fd[e] = f; 95 if (i) 96 fd[e] = fd[e] + pred[i - 1]; 97 if (i < s - 1) 98 fd[e] = fd[e] + sufd[i + 1]; 99 dp2(e); 100 } 101 } 102 103 int ceil2(int x) { 104 return (x + 1) >> 1; 105 } 106 107 inline void solve() { 108 dp1(1, 0); 109 dp2(1); 110 for (int i = 2; i <= n; i++) 111 res = min(res, max(ceil2(fd[i].mxlen), ceil2(fu[i].mxlen))); 112 if (n == 1) 113 res = 0; 114 printf("%d\n", res); 115 } 116 117 int main() { 118 // freopen("ob.in", "r", stdin); 119 // freopen("ob.out", "w", stdout); 120 init(); 121 solve(); 122 return 0; 123 }