省选模拟赛 2018.8.11

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

Problem A 闪电攻击

题目大意

  SR操纵一架战机。在时刻$a_{i}$初会出现第$i$号敌机,它始终与SR的战机保持的距离为$d_{i}$,在时刻$b_{i}$末它会发射一枚导弹摧毁SR的战机。SR可以选择在任意时间花费$x$的能量消灭所有距离不超过$x$的敌机。问SR在保证他的安全的情况下最少需要多少能量。

  开始一直想的是按顺序做决策。然后发现和gg。

  但是如果状态改为一个值域区间内的敌机全部消灭掉,那么转移就轻松了许多。

  用$f[l][r]$表示将可以被攻击的时间段是$[l, r]$的子区间的战机全部消灭最少需要的能量。

  考虑其中$d$最大的敌机一定是被花费能量为$d$的攻击直接消灭掉的。

  另外将攻击的时刻移动到端点不会更劣。(然后就可以离散化了)

  枚举在这中间是在哪个端点时攻击。把区间分成$[l, k - 1]$和$[k + 1, r]$。

  时间复杂度$O(n^3)$。

Code

 1 #include <algorithm>
 2 #include <iostream>
 3 #include <cstdlib>
 4 #include <cstdio>
 5 using namespace std;
 6 typedef bool boolean;
 7 
 8 const int N = 305;
 9 const signed int inf = (signed) (~0u >> 1);
10 
11 typedef class Enemy {
12     public:
13         int l, r, d;
14 
15         Enemy(int l = 0, int r = 0, int d = 0):l(l), r(r), d(d) {}
16 }Enemy;
17 
18 int n;
19 Enemy es[N];
20 int ps[N << 1];
21 int f[N << 1][N << 1];
22 boolean vis[N << 1][N << 1];
23 
24 inline void init() {
25     scanf("%d", &n);
26     for (int i = 1, l, r, d; i <= n; i++) {
27         scanf("%d%d%d", &l, &r, &d);
28         ps[(i << 1) - 1] = l, ps[(i << 1)] = r;
29         es[i] = Enemy(l, r, d);
30     }
31 }
32 
33 int dfs(int l, int r) {
34     if (l > r)
35         return 0;
36     if (vis[l][r])
37         return f[l][r];
38     vis[l][r] = true;
39     int mxd = -1, id = -1;
40     for (int i = 1; i <= n; i++)
41         if (es[i].l >= l && es[i].r <= r && es[i].d > mxd)
42             mxd = es[i].d, id = i;
43     if (id == -1)
44         return f[l][r] = 0;
45     f[l][r] = inf;
46     for (int i = 1; i <= n; i++) {
47         if (!(es[i].l >= l && es[i].r <= r))
48             continue;
49         if (es[i].l >= es[id].l && es[i].l <= es[id].r)
50             f[l][r] = min(f[l][r], dfs(l, es[i].l - 1) + dfs(es[i].l + 1, r) + es[id].d);
51         if (es[i].r >= es[id].l && es[i].r <= es[id].r)
52             f[l][r] = min(f[l][r], dfs(l, es[i].r - 1) + dfs(es[i].r + 1, r) + es[id].d);
53     }
54     return f[l][r];
55 }
56 
57 inline void solve() {
58     sort(ps + 1, ps + n * 2 + 1);
59     for (int i = 1; i <= n; i++) {
60         es[i].l = lower_bound(ps + 1, ps + n * 2 + 1, es[i].l) - ps;
61         es[i].r = lower_bound(ps + 1, ps + n * 2 + 1, es[i].r) - ps;
62     }
63 
64     for (int i = 1; i <= n; i++)
65         for (int j = i + 1; j <= n; j++)
66             dfs(min(es[i].l, es[j].l), max(es[i].r, es[j].r));
67     
68     int l = inf, r = -inf;
69     for (int i = 1; i <= n; i++)
70         l = min(l, es[i].l), r = max(r, es[i].r);
71     printf("%d\n", dfs(l, r));
72 }
73 
74 int main() {
75     init();
76     solve();
77     return 0;
78 }
Problem A

Problem B 最小生成树

题目大意

  给定$n$个点$m$条边的无向带权图,每次给定$l, r$,询问边权在$[l, r]$之间的边在保证连通块个数尽量小的情况下最小生成森林。(强制在线)

  考虑Kruskal的过程,发现$r$其实没有任何用。原因是我可以把选择的边存下来,记录边权的前缀和,然后询问的时候二分一下。

  于是先将边排序,然后从大到小加入每一条边。记录每次的最小生成树中选择的边。

  由于数据范围很小可以直接暴力。出题者考虑到要让代码量和谐,所以没有把数据范围变成$n, m \leqslant 10^5$。

  如果改成这个毒瘤数据范围,那么就把dfs找环上最小边改成LCT,复制数组改成可持久化线段树。

  由于我比较懒,出题人也比较懒,出随机数据,于是我写了一个期望$O(nm\log m + q\log n)$的做法(最坏$O(m^{2})$,233)。

  考场上粘另一道题的读入优化板子,没处理负数,强制在线的一半的点全挂掉了qwq。处理一下读入负数就过了。

Code

  1 #include <algorithm>
  2 #include <iostream>
  3 #include <cstdlib>
  4 #include <cstdio>
  5 #include <ctime>
  6 using namespace std;
  7 typedef bool boolean;
  8 #define pii pair<int, int>
  9 #define fi first
 10 #define sc second
 11 
 12 typedef class IO {
 13     protected:
 14         const static int Limit = 65536;
 15         FILE* file;
 16 
 17         int ss, st;
 18         char buf[Limit];
 19     public:
 20         IO():file(NULL)    {    };
 21         IO(FILE* file):file(file) {    }
 22 
 23         void open(FILE *file) {
 24             this->file = file;
 25         }
 26 
 27         char pick() {
 28             if (ss == st)
 29                 st = fread(buf, 1, Limit, stdin), ss = 0;//, cerr << "Str: " << buf << "ED " << st << endl;
 30             return buf[ss++];
 31         }
 32 }IO;
 33 
 34 #define digit(_x) ((_x) >= '0' && (_x) <= '9')
 35 
 36 IO& operator >> (IO& in, int& u) {
 37     char x;
 38     int aflag = 1;
 39     while (~(x = in.pick()) && !digit(x) && x != '-');
 40     if (x == '-') {
 41         aflag = -1;
 42         x = in.pick();
 43     }
 44     for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
 45     u = u * aflag;
 46     return in;
 47 }
 48 
 49 IO in; 
 50 
 51 typedef class Edge {
 52     public:
 53         int u, v, w;
 54 
 55         boolean operator < (Edge b) const {
 56             return w < b.w;
 57         }
 58 }Edge;
 59 
 60 const int N = 505, M = 20005;
 61 
 62 int n, m, q, alpha;
 63 int* wss, *uf;
 64 Edge* es;
 65 pii res[M][N];
 66 int fins[M];
 67 
 68 int find(int x) {
 69     return (x == uf[x]) ? (x) : (uf[x] = find(uf[x]));
 70 }
 71 
 72 inline void init() {
 73     in >> n >> m;
 74     uf = new int[(n + 1)];
 75     es = new Edge[(m + 1)];
 76     wss = new int[(m + 1)];
 77     for (int i = 1; i <= m; i++) {
 78         in >> es[i].u >> es[i].v >> es[i].w;
 79         wss[i] = es[i].w;
 80     }
 81 }
 82 
 83 inline void solve() {
 84     sort(es + 1, es + m + 1);
 85     sort(wss + 1, wss + m + 1);
 86     fins[0] = n - 1;
 87     for (int i = 1; i <= m; i++) {
 88         int fin = 0;
 89         for (int j = 1; j <= n; j++)
 90             uf[j] = j;
 91         for (int j = i; j <= m && fin < fins[i - 1]; j++) {
 92             int u = es[j].u, v = es[j].v;
 93             if (find(u) != find(v)) {
 94                 uf[find(u)] = find(v);
 95                 fin++;
 96                 res[i][fin].fi = res[i][fin - 1].fi + es[j].w;
 97                 res[i][fin].sc = es[j].w;
 98             }
 99         }
100         fins[i] = fin;
101     }
102 
103     in >> q >> alpha;
104     int L, R, lastans = 0, s;
105     while (q--) {
106         in >> L >> R;
107         L = lastans * alpha + L, R = L + R;
108         int l = 1, r = m, mid;
109         while (l <= r) {
110             mid = (l + r) >> 1;
111             if (wss[mid] >= L)
112                 r = mid - 1;
113             else
114                 l = mid + 1;
115         }
116         s = r + 1;
117         if (s > m) {
118             puts("0");
119             continue;
120         }
121 
122         l = 1, r = fins[s];
123         while (l <= r) {
124             mid = (l + r) >> 1;
125             if (res[s][mid].sc <= R)
126                 l = mid + 1;
127             else
128                 r = mid - 1;
129         }
130         printf("%d\n", lastans = res[s][l - 1].fi);
131     }
132 }
133 
134 int main() {
135     freopen("mst.in", "r", stdin);
136     freopen("mst.out", "w", stdout);
137     init();
138     solve();
139     return 0;
140 }
Problem B

Problem C 最佳南瓜

题目大意

  有$n$个数的数组和$m$个操作,每次操作分两步:

  • 区间加上1
  • 询问整个数组的中位数

  (保证$n$为奇数)

  直接考虑分块。

  每个块维护快内的数排序后的数组(保留原来的下标)。  

  修改的时候区间内打tag,两端暴力重构。重构的时候用瑞士轮的方法归并排排序。

  询问整个数组的中位数时二分就挂了。由于修改比较特殊,所以答案要么不变要么增加1。

  因此每次暴力判断答案是否增加1。判断的话就一个一个块地跳,每个块二分一下多少数小于等于当前答案。

  然后调整一下块大小(显然不能是$\sqrt{n}$)就给过了。

Code

  1 #include <algorithm>
  2 #include <iostream>
  3 #include <cstdlib>
  4 #include <cstdio>
  5 #include <ctime>
  6 using namespace std;
  7 typedef bool boolean;
  8 #define pii pair<int, int>
  9 #define fi first
 10 #define sc second
 11 
 12 typedef class IO {
 13     protected:
 14         const static int Limit = 65536;
 15         FILE* file;
 16 
 17         int ss, st;
 18         char buf[Limit];
 19     public:
 20         IO():file(NULL)    {    };
 21         IO(FILE* file):file(file) {    }
 22 
 23         void open(FILE *file) {
 24             this->file = file;
 25         }
 26 
 27         char pick() {
 28             if (ss == st)
 29                 st = fread(buf, 1, Limit, stdin), ss = 0;//, cerr << "Str: " << buf << "ED " << st << endl;
 30             return buf[ss++];
 31         }
 32 }IO;
 33 
 34 #define digit(_x) ((_x) >= '0' && (_x) <= '9')
 35 
 36 IO& operator >> (IO& in, int& u) {
 37     char x;
 38     while (~(x = in.pick()) && !digit(x));
 39     for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
 40     return in;
 41 }
 42 
 43 IO in;
 44 
 45 const int N = 1e5 + 5, cs = 450;
 46 
 47 pii b1[cs + 1], b2[cs + 1];
 48 
 49 typedef class Chunk {
 50     public:
 51         int tag, s;
 52         pii cr[cs + 1];
 53 
 54         Chunk():tag(0), s(0) {    }
 55         
 56         void init(int s, int* ar) {
 57             this->s = s;
 58             for (int i = 0; i < s; i++)
 59                 in >> cr[i].fi, cr[i].sc = i, ar[i] = cr[i].fi;
 60             sort(cr, cr + s);
 61         }
 62 
 63         void pushDown() {
 64             if (!tag)
 65                 return;
 66             for (int i = 0; i < s; i++)
 67                 cr[i].fi += tag;
 68             tag = 0;
 69         }
 70 
 71         void add(int l, int r) {
 72             pushDown();
 73             int tp1 = 0, tp2 = 0, p = 0;
 74             for (int i = 0; i < s; i++)
 75                 if (cr[i].sc >= l && cr[i].sc <= r)
 76                     cr[i].fi++, b1[++tp1] = cr[i];
 77                 else
 78                     b2[++tp2] = cr[i];
 79             int p1 = 1, p2 = 1;
 80             while (p1 <= tp1 || p2 <= tp2) {
 81                 if (p1 <= tp1 && (p2 > tp2 || b1[p1].fi <= b2[p2].fi))
 82                     cr[p++] = b1[p1++];
 83                 else
 84                     cr[p++] = b2[p2++];
 85             }
 86         }
 87 
 88         int query(int x) {        // How many numbers are not greater than x
 89             x -= tag;
 90             int l = 0, r = s - 1;
 91             while (l <= r) {
 92                 int mid = (l + r) >> 1;
 93                 if (cr[mid].fi <= x)
 94                     l = mid + 1;
 95                 else
 96                     r = mid - 1;
 97             }
 98             return l;
 99         }
100 }Chunk;
101 
102 int n, m, hn, cc;
103 int cmid = 0;
104 int ar[N];
105 Chunk chs[N / cs + 10];
106 
107 inline void init() {
108     in >> n >> m;
109     hn = (n >> 1) + 1;
110     for (int l = 0, r; l < n; l += cs, cc++) {
111         r = min(l + cs, n) - 1;
112         chs[l / cs].init(r - l + 1, ar + l);
113     }
114 }
115 
116 int calc(int x) {
117     int rt = 0;
118     for (int i = 0; i < cc && rt < hn; i++)
119         rt += chs[i].query(x);
120     return rt;
121 }
122 
123 inline void solve() {
124     sort(ar, ar + n);
125     cmid = ar[hn - 1];
126     for (int i = 1, l, r, lid = 0, rid = 0; i <= m; i++) {
127         in >> l >> r;
128         l--, r--;
129         lid = l / cs, rid = r / cs;
130         l %= cs, r %= cs;
131         if (lid == rid)
132             chs[lid].add(l, r);
133         else {
134             for (int j = lid + 1; j < rid; j++)
135                 chs[j].tag++;
136             chs[lid].add(l, chs[lid].s - 1);
137             chs[rid].add(0, r);
138         }
139         while (calc(cmid) < hn)
140             cmid++;
141         printf("%d\n", cmid);
142     }
143 }
144 
145 int main() {
146     freopen("pumpkin.in", "r", stdin);
147     freopen("pumpkin.out", "w", stdout);
148     init();
149     solve();
150     return 0;
151 }
Problem C

猜你喜欢

转载自www.cnblogs.com/yyf0309/p/9482880.html
今日推荐