2018.9.22 NOIP模拟赛

*注意:这套题目应版权方要求,不得公示题面。

  因为业务水平下滑太严重,然后被踢出清华集训模拟,去和高一考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 A

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 B

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 }
Problem C

猜你喜欢

转载自www.cnblogs.com/yyf0309/p/9690502.html