T1:Star Way To Heaven

  题目原型:欧基里德生成树

考虑转化题意为,从左侧到右侧的所有路径中,选择一条,使得路径上所有点距离star的最小距离最大

首先容易发现对于任意两颗star,走其连线的中点一定最优,那么问题再次转化

将所有star连线后,选择一条经过若干star中点路径满足上述条件

  考虑首先满足最小距离,考虑连接上下边界与star形成的屏障中选择一条最短路径

容易发现这条最短路径一定位于有star与上下边界连边构成的最小生成树上

考虑数据范围6e3,kruskal O(mlogm)必然TLE,prim(n^2) 刚好可以

于是在prim过程中记录最小生成树最大边权即可

  需要注意的是,本题并不是求解完整的最小生成树,而是其中的一部分

我们只需要考虑能从左边到右边路径最小权值的最大值,换言之,该路径的关键为

上下边界与star形成的屏障(线性结构),而非最小生成树的树形结构,因此

在当prim拓展到连接上边界的边时便停止prim,因为此时最小边的最大值已经找到

继续拓展会更新res导致错误

代码如下:

NOIP模拟十六_c++NOIP模拟十六_单调栈_02
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define I int
 4 #define D double
 5 #define V void
 6 const I MAXK = 6e3 + 5;
 7 I n,m,k;
 8 D res,X[MAXK],Y[MAXK];
 9 namespace PRIM {
10     D dis[MAXK];
11     bool jud[MAXK];
12     inline V prim () {
13         while (1) {
14             I x(0);
15             for (I j(1);j <= k; ++ j)
16               if (!jud[j] && (!x || dis[j] < dis[x]))
17                 x = j;
18             jud[x] = 1;
19             res = max (res,dis[x]);
20             if (x == k) break;
21             for (I j(1);j < k; ++ j)
22               if (!jud[j])
23                 dis[j] = min (dis[j],sqrt ((X[j] - X[x]) * (X[j] - X[x]) + (Y[j] - Y[x]) * (Y[j] - Y[x])));
24             dis[k] = min (dis[k],Y[x]);
25         }
26     }
27 }
28 signed main () {
29     cin >> n >> m >> k;
30     for (I i(1);i <= k; ++ i) {
31         cin >> X[i] >> Y[i];
32         PRIM :: dis[i] = m - Y[i];
33     }    
34     PRIM :: dis[++k] = m;
35     PRIM :: prim ();
36     printf ("%.10lf",res / 2);
37 }
View Code

T2:God Knows

  同样,线性序列下元素均匀变化,考虑递推,发现新元素加入后

对其造成影响的为序列中能拓展到当前位置的元素

  也就是对于元素i的插入,对其造成影响的为元素j满足:

不存在k是的j<k<i且pj<pk<pi,发现问题转化为求解最小带权极大上升子序列O(n^2)

考虑如何优化,同样在转移时具备整体性,于是考虑数据结构优化DP

进行整体转移,线段树维护单调栈即可

  线段树维护数据结构与序列,需要考虑左右儿子递推关系与影响,

同时为了保证O(logn)的复杂度,需适当记录转移需要的信息

代码如下:

NOIP模拟十六_c++NOIP模拟十六_单调栈_02
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define I int
 4 #define V void
 5 #define lid id << 1
 6 #define rid id << 1 | 1
 7 const I MAXN = 2e5 + 3;
 8 const I INT = 0x3fffffff;
 9 namespace ST {
10     I sta[MAXN << 2],dp[MAXN << 2],nxt[MAXN << 2];  //sta记录右儿子栈底元素
11     I head;                                         //nxt记录左儿子被右儿子更新后的最小元素 用于更新dp(降低时间复杂度)
12     V found (I id,I l,I r) {                        //dp记录栈内最小元素
13         sta[id] = -INT;
14         dp[id] = nxt[id] = INT;
15         if (l == r) return ; 
16         I mid (l + r >> 1);
17         found (lid,l,mid),found (rid,mid + 1,r);
18     }
19     I maintain (I id,I l,I r,I head) {              //维护左右儿子单调关系(本质上是区间查询)返回满足单调性的最小元素
20         if (l == r) return sta[id] > head ? dp[id] : INT;
21         I mid (l + r >> 1);
22         return sta[rid] > head ? min (nxt[lid],maintain (rid,mid + 1,r,head)) : maintain (lid,l,mid,head);  
23     }                                               //本质上是返回关键字以head结尾的单调栈内最小元素
24     V insert (I id,I l,I r,I pos,I key,I val) {
25         if (l == r) {
26             dp[id] = val;
27             sta[id] = key;
28             return ;
29         }
30         I mid (l + r >> 1);
31         pos <= mid ? insert(lid,l,mid,pos,key,val) : insert(rid,mid + 1,r,pos,key,val);
32         nxt[lid] = maintain (lid,l,mid,sta[rid]);   //维护插入新元素后满足单调性的左儿子最小元素(用于更新栈内最小元素)
33         sta[id] = max (sta[lid],sta[rid]);          //维护区间单调栈内最大元素(用于索引)
34         dp[id] = min (dp[rid],nxt[lid]);            //维护区间单调栈最小元素(即为求解的元素)
35     }
36     I sec_que (I id,I l,I r,I ql,I qr) {
37         I res(INT);
38         if (ql <= l && r <= qr) {
39             res = maintain (id,l,r,head);           //查询当前区间内以当前最大关键字结尾的最小元素
40             head = max (head,sta[id]);              //更新单调栈最大关键字
41             return res;
42         }    
43         I mid (l + r >> 1);
44         if (qr >  mid) res = min (res,sec_que (rid,mid + 1,r,ql,qr));   //先查询右侧区间记录单调栈最大关键字
45         if (ql <= mid) res = min (res,sec_que (lid,l,mid,ql,qr));
46         return res;
47     }
48 }
49 namespace LYM {
50     I n,it[MAXN],cost[MAXN];
51     I res; 
52     V init () {
53         cin >> n;
54         for (I i(1);i <= n; ++ i)
55             cin >> it[i];
56         for (I i(1);i <= n; ++ i)
57             cin >> cost[i];    
58         it[++n] = n;
59     }
60     I outit () {
61         ST :: found (1,0,n);                    
62         ST :: insert (1,0,n,0,0,0);                 //插入0与n+1便于直接统计答案
63         for (I i(1);i <= n; ++ i) {
64             ST :: head = -1;
65             res = ST :: sec_que (1,0,n,0,it[i] - 1) + cost[i];
66             ST :: insert (1,0,n,it[i],i,res); 
67         }
68         cout << res << endl;
69         return 0;
70     }   
71 }
72 signed main () {    
73     LYM :: init ();
74     LYM :: outit ();
75 }
View Code

T3:Lost My Music

  题目很容易理解,考虑转移,每个点都会对其子节点造成贡献

暴力转移O(nlogn)~O(n^2),但是发现O(nlogn)做法会被菊花图卡

成O(n^2),但是在此种情况下,每个点贡献都计算到且无重复,仍然无法通过

也就是说,存在一部分点作出无用贡献,考虑如何避免对这些点的计算

  观察题中式子发现可以转化为斜率式,考虑将每个点deep与c当作两维

进行斜率计算,而我们要保证斜率最小,于是考虑单调性

  进行dfs时深度单调增加,此时对于当前节点若存在一子节点斜率式大于其父节点

斜率式,那么其一定不优,在计算之后节点时就可以忽略,同时需要记录当前计算出的

无用节点,在更新父亲时直接略过(严格意义上此处并非父亲,而是可能造成贡献的

深度最大的节点)

代码如下:

NOIP模拟十六_c++NOIP模拟十六_单调栈_02
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define I int
 4 #define D double
 5 #define V void
 6 const I MAXN = 5e5 + 3;
 7 namespace F_MAP {
 8     I tot,head[MAXN];
 9     struct NODE {
10         I to,nxt;
11     }node[MAXN];
12     inline V found (I x,I y) {
13         node[++tot].to = y,node[tot].nxt = head[x],head[x] = tot;
14     }
15 }
16 namespace F_DFS {
17     I f[MAXN][20],d[MAXN],c[MAXN];
18     D res[MAXN];
19     inline D fir (I x,I y) {
20         return 1.0 * (c[y] - c[x]) / (d[x] - d[y]);
21     }
22     V dfs (I x) {
23         I father(f[x][0]);
24         for (I i(19); ~i ; -- i) {
25           if (f[father][i] <= 1) continue;
26           if (fir (x,f[father][i]) >= fir (x,f[f[father][i]][0])) father = f[f[father][i]][0];
27         }
28         if (father > 1 && fir (x,father) >= fir (x,f[father][0])) father = f[father][0];
29         f[x][0] = father;
30         res[x] = fir (x,father);
31         for (I i(1);i < 20; ++ i) f[x][i] = f[f[x][i - 1]][i - 1];
32         for (I i(F_MAP :: head[x]),y(F_MAP :: node[i].to); i ;i = F_MAP :: node[i].nxt,y = F_MAP :: node[i].to)
33           d[y] = d[x] + 1, dfs (y);
34     }
35 }
36 namespace LYM {
37     I n;
38     inline V init () {
39         cin >> n;
40         for (I i(1);i <= n; ++ i)
41           cin >> F_DFS :: c[i];
42         for (I i(2);i <= n; ++ i) {
43           cin >> F_DFS :: f[i][0];
44           F_MAP :: found (F_DFS :: f[i][0],i);
45         }
46     }
47     inline V outit () {
48         F_DFS :: dfs (1);
49         for (I i(2);i <= n; ++ i)
50           printf ("%.10lf\n",F_DFS :: res[i]);   
51     }
52 }
53 signed main () {
54     LYM :: init ();
55     LYM :: outit ();
56 }
View Code