[题解]NOIP2017 Day2 Solution

来源:互联网 发布:haskell linux 编辑:程序博客网 时间:2024/05/22 09:39

感觉Day2比2013还恶心……后来看了清华爷zzq的讲解,才搞出T2。

T1 cheese

模拟题。把可以互相到达的球连边后,用bfs或并查集判断是否连通。
注意细(keng)节(dian):在极端情况下,两点之间的距离会达到1.21019(爆long long),所以要用unsigned long long或double。
代码:

#include <cmath>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;inline int read() {    int res = 0; bool bo = 0; char c;    while (((c = getchar()) < '0' || c > '9') && c != '-');    if (c == '-') bo = 1; else res = c - 48;    while ((c = getchar()) >= '0' && c <= '9')        res = (res << 3) + (res << 1) + (c - 48);    return bo ? ~res + 1 : res;}const int N = 1005; const double eps = 1e-8;int n, h, r, X[N], Y[N], Z[N], fa[N]; double DIS;inline int cx(const int &x) {    if (fa[x] != x) fa[x] = cx(fa[x]);    return fa[x];}inline void zm(const int &x, const int &y) {    int ix = cx(x), iy = cx(y);    if (ix != iy) fa[iy] = ix;}inline bool check(const int &i, const int &j) {    if (X[i] - X[j] > (r << 1) || Y[i] - Y[j] > (r << 1) ||    Z[i] - Z[j] > (r << 1)) return 0;    double dis = 1.0 * (X[i] - X[j]) * (X[i] - X[j]) + 1.0 *    (Y[i] - Y[j]) * (Y[i] - Y[j]) + 1.0 * (Z[i] - Z[j]) * (Z[i] - Z[j]);    return abs(DIS - dis) <= eps || dis < DIS;}void work() {    int i, j; n = read(); h = read(); r = read(); DIS = 4.0 * r * r;    for (i = 1; i <= n + 2; i++) fa[i] = i;    for (i = 1; i <= n; i++) X[i] = read(), Y[i] = read(), Z[i] = read();    for (i = 1; i < n; i++) for (j = i + 1; j <= n; j++)        if (check(i, j)) zm(i, j);    for (i = 1; i <= n; i++) {        if (Z[i] <= r) zm(n + 1, i);        if (Z[i] >= h - r) zm(i, n + 2);    }    puts(cx(n + 1) == cx(n + 2) ? "Yes" : "No");}int main() {    //freopen("cheese.in", "r", stdin);    //freopen("cheese.out", "w", stdout);    int T = read();    while (T--) work();    return 0;}

T2 treasure

看到12的数据范围,容易想到状压DP,可以建立如下模型:
f[u][dep][S]表示u的深度为depu的子树内包含的点集为Su的子树的最小代价。
容易推出转移:
f[u][dep][S]=minSSuS,vS(f[v][dep+1][S]+f[u][dep][SS]+val<u,v>dep)
其中val<u,v><u,v>的边权。枚举子集,实现即为:

for (i = (S - 1) & S; i; i = (i - 1) & S);

这时候复杂度为O(3nn3),考虑优化,可以发现,枚举v特别浪费时间。所以容易想到,把f[v][dep+1][S]+val<u,v>dep看成一个整体考虑,也就是,令:
g[u][dep][S]=min(f[v][dep][S]+val<u,v>(dep1))
这时候,转移方程优化为:
f[u][dep][S]=minSS,uS(f[u][dep[SS]+g[u][dep+1][S])
复杂度O(3nn2)
代码:

#include <cmath>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;inline int read() {    int res = 0; bool bo = 0; char c;    while (((c = getchar()) < '0' || c > '9') && c != '-');    if (c == '-') bo = 1; else res = c - 48;    while ((c = getchar()) >= '0' && c <= '9')        res = (res << 3) + (res << 1) + (c - 48);    return bo ? ~res + 1 : res;}const int N = 16, C = (1 << 12) + 5, INF = 0x3f3f3f3f;int n, m, G[N][N], f[N][N][C], g[N][N][C];void dfs(int u, int dep, int S) {    if (f[u][dep][S] != -1) return;    if (S == (1 << u - 1)) return (void) (f[u][dep][S] = g[u][dep][S] = 0);    int i, j; f[u][dep][S] = g[u][dep][S] = INF;    for (j = (S - 1) & S; j; j = (j - 1) & S) {        if ((j >> u - 1) & 1) continue; dfs(u, dep, S - j); dfs(u, dep + 1, j);        f[u][dep][S] = min(f[u][dep][S], f[u][dep][S - j] + g[u][dep + 1][j]);    }    for (i = 1; i <= n; i++) {        if (!((S >> i - 1) & 1) || G[u][i] == INF) continue;        dfs(i, dep, S);        g[u][dep][S] = min(g[u][dep][S], f[i][dep][S] + G[u][i] * (dep - 1));    }}int main() {    //freopen("treasure.in", "r", stdin);    //freopen("treasure.out", "w", stdout);    memset(G, INF, sizeof(G)); memset(f, -1, sizeof(f));    int i, j, k, x, y, z, ans = INF; n = read(); m = read();    for (i = 1; i <= m; i++) {        x = read(); y = read(); z = read();        G[x][y] = min(G[x][y], z);        G[y][x] = min(G[y][x], z);    }    for (i = 1; i <= n; i++) ans =        min(ans, (dfs(i, 1, (1 << n) - 1), f[i][1][(1 << n) - 1]));    cout << ans << endl;    return 0;}

T3 phalanx

好像是NOIP有史以来第一道数据结构题。
蒟蒻用的是Splay的做法,即用n棵Splay维护每一行的前m1个元素,再用一棵维护第m列的元素,这样就是Splay的区间操作了。
考虑到空间问题,对于前n棵Splay,可以不建出所有的点,而是对于没有被改过(编号连续)的位置,在对应的点上维护一个区间,操作时分裂区间即可。(相似题目:BZOJ 3595)
据说标算是树状数组。
代码:

#include <cmath>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;inline int read() {    int res = 0; bool bo = 0; char c;    while (((c = getchar()) < '0' || c > '9') && c != '-');    if (c == '-') bo = 1; else res = c - 48;    while ((c = getchar()) >= '0' && c <= '9')        res = (res << 3) + (res << 1) + (c - 48);    return bo ? ~res + 1 : res;}typedef long long ll;const int N = 3e6 + 5;int n, m, q, T, fa[N], lc[N], rc[N], rt[N]; ll sze[N], L[N], R[N];int which(int x) {return rc[fa[x]] == x;}void upt(int x) {    sze[x] = R[x] - L[x] + 1;    if (lc[x]) sze[x] += sze[lc[x]];    if (rc[x]) sze[x] += sze[rc[x]];}void rotate(int x) {    int y = fa[x], z = fa[y], b = lc[y] == x ? rc[x] : lc[x];    if (z) (lc[z] == y ? lc[z] : rc[z]) = x;    fa[x] = z; fa[y] = x; if (b) fa[b] = y;    if (lc[y] == x) rc[x] = y, lc[y] = b;    else lc[x] = y, rc[y] = b; upt(y); upt(x);}void splay(int x, int tar, int r) {    while (fa[x] != tar) {        if (fa[fa[x]] != tar) {            if (which(x) == which(fa[x])) rotate(fa[x]);            else rotate(x);        }        rotate(x);    }    if (!tar) rt[r] = x;}void BuildRow(int row) {    int i, x = ++T; L[rt[row] = x] = 1ll * (row - 1) * m + 1;    R[x] = 1ll * row * m - 1; fa[lc[x] = ++T] = x; fa[rc[x] = ++T] = x;    sze[lc[x]] = sze[rc[x]] = 1; L[lc[x]] = R[lc[x]] = 1ll * (row - 1) * m;    L[rc[x]] = R[rc[x]] = 1ll * row * m; upt(x);}int BuildLast(int l, int r) {    int mid = l + r >> 1, x = ++T;    if (l == 0 && r == n + 1) rt[n + 1] = x;    if (l < mid) fa[lc[x] = BuildLast(l, mid - 1)] = x;    if (mid < r) fa[rc[x] = BuildLast(mid + 1, r)] = x;    L[x] = R[x] = 1ll * mid * m; return upt(x), x;}void build() {    int i; for (i = 1; i <= n; i++) BuildRow(i);    BuildLast(0, n + 1);}void DivLeft(int x, ll mid) {    int y = lc[x], z = ++T;    fa[lc[x] = z] = x; if (y) fa[lc[z] = y] = z;    L[z] = L[x]; L[x] = mid + 1; R[z] = mid;    upt(z); upt(x);}void DivRight(int x, ll mid) {    int y = rc[x], z = ++T;    fa[rc[x] = z] = x; if (y) fa[rc[z] = y] = z;    R[z] = R[x]; R[x] = mid - 1; L[z] = mid;    upt(z); upt(x);}void Divide(int x, ll mid) {    ll tl = L[x], tr = R[x];    if (tl < mid) DivLeft(x, mid - 1);    if (mid < tr) DivRight(x, mid + 1);}void Find(int wh, ll rk, int tar) {    int x = rt[wh]; while (x) {        ll tmp = lc[x] ? sze[lc[x]] : 0;        if (rk > tmp && rk <= tmp + (R[x] - L[x] + 1))            return splay(x, tar, wh), Divide(x, L[x] + rk - tmp - 1);        else if (rk <= tmp) x = lc[x];        else rk -= tmp + (R[x] - L[x] + 1), x = rc[x];    }}ll query(int x, int y) {    ll res; Find(x, y, 0); Find(x, y + 2, rt[x]);    int u = rt[x], v = rc[u], w = lc[v]; res = L[w];    fa[w] = lc[v] = 0; upt(v); upt(u); Find(n + 1, x, 0);    Find(n + 1, x + 2, rt[n + 1]); int r = rt[n + 1], s = rc[r], t = lc[s];    fa[t] = lc[s] = 0; upt(s); upt(r);    Find(x, m - 1, 0); Find(x, m, rt[x]);    fa[lc[rc[rt[x]]] = t] = rc[rt[x]]; upt(rc[rt[x]]); upt(rt[x]);    Find(n + 1, n, 0); Find(n + 1, n + 1, rt[n + 1]);    fa[lc[rc[rt[n + 1]]] = w] = rc[rt[n + 1]];    return upt(rc[rt[n + 1]]), upt(rt[n + 1]), res;}ll ask(int x) {    ll res; Find(n + 1, x, 0); Find(n + 1, x + 2, rt[n + 1]);    int u = rt[n + 1], v = rc[u], w = lc[v]; res = L[w];    fa[w] = lc[v] = 0; upt(v); upt(u);    Find(n + 1, n, 0); Find(n + 1, n + 1, rt[n + 1]);    fa[lc[rc[rt[n + 1]]] = w] = rc[rt[n + 1]];    return upt(rc[rt[n + 1]]), upt(rt[n + 1]), res;}int main() {    //freopen("phalanx.in", "r", stdin);    //freopen("phalanx.out", "w", stdout);    int x, y; n = read(); m = read(); q = read(); build();    while (q--) {        x = read(); y = read();        printf("%lld\n", y == m ? ask(x) : query(x, y));    }    return 0;}
原创粉丝点击