HNOI2015解题报告

来源:互联网 发布:诸暨行知小学怎么样 编辑:程序博客网 时间:2024/05/20 23:57

HNOI2015解题报告

Author: Pengyihao


Day1 T1 亚瑟王


思路

f[i][j] 表示 i 一共获得了 j 次“机会”的概率。

注意这里的“机会”,是指有多少轮中,它前面的所有卡牌,要么在之前的轮中发动过,要么在这一轮中因为运气没有发动。

这里的“机会”不与自己有没有发动相关。所以就算在之前某轮中发动过,这一轮轮到它的时候贡献依然要增加。

于是有 f[i][j]=f[i1][j+1](1(1p[i1])j)+f[i1][j](1p[i1])j

ans=ni=1rj=1f[i][j]d[i](1(1p[i])j)


代码

#include <bits/stdc++.h>typedef long long LL;#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)template <typename Tp> void in(Tp &x) {    char ch = getchar(); x = 0;    while (ch < '0' || ch > '9') ch = getchar();    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();}template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}const int MAXN = 230, MAXR = 140;int T, n, r, d[MAXN];double p[MAXN], f[MAXN][MAXR];double power(double x, int y){    double ret = 1;    while (y) {        if (y & 1) ret *= x;        x *= x;        y >>= 1;    }    return ret;}int main(){    freopen("arthur.in", "r", stdin);    freopen("arthur.out", "w", stdout);    in(T);    while (T--) {        in(n); in(r);        FOR(i, 1, n) {            scanf("%lf", &p[i]); in(d[i]);        }        memset(f, 0, sizeof f);        f[1][r] = 1;        FOR(i, 2, n) FOR(j, 1, r) {            f[i][j] = 0;            f[i][j] += f[i - 1][j + 1] * (1 - power(1 - p[i - 1], j + 1));            f[i][j] += f[i - 1][j]     * power(1 - p[i - 1], j);        }        double ans = 0;        FOR(i, 1, n) FOR(j, 1, r) {            ans += f[i][j] * (1 - power(1 - p[i], j)) * d[i];        }        printf("%.10lf\n", ans);    }    return 0;}

Day1 T2 接水果


思路

首先,我们求出每个点的dfs序。

然后对于一条x<–>y的路径,如果有路径包含它,那么有

  1. 如果 lca(x,y)=xory,那么只需要这条路径的一个端点在深度大的点的子树中,另一个端点不在“深度小的点往深度大的点的方向上的第一个点”的子树中即可。

  2. 否则,那么只需要这条路径的两个端点分别在 xy 的子树中即可。

我们令这条包含x<–>y的路径的两个端点中dfs序小的点为 a,dfs序大的点为 b

对于上面两种情况中的任意一种情况,这种情况中 ab 所在的子树互不相交,为两个不相交的dfs序区间(不在子树中的情况我们可以转化为在它的补集的区间的情况)。

于是我们就把问题转化为了,每个盘子对应一对(或两对,因为有“不在子树”)区间,然后对于每个水果的两个点,两个区间分别包含其两个点的盘子中,权值第 k 小的。

我们可以把一维转化成 x 坐标,另一维转化成 y 坐标,于是问题就转化为了包含一个点的矩形中第 k 小的。

于是可以用扫描线+树状数组套主席树解决。


代码

#include <bits/stdc++.h>using std::map;typedef long long LL;#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)#define debug(...) fprintf(stderr, __VA_ARGS__)template <typename Tp> void in(Tp &x) {    char ch = getchar(); x = 0;    while (ch < '0' || ch > '9') ch = getchar();    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();}template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}const int MAXN = 100010;const int appear = 0, disappear = 2, query = 1;int n, p, q, ev_tot;int cnt, head[MAXN], data[MAXN << 1], nxt[MAXN << 1];int INDEX, dfn[MAXN], ed[MAXN], depth[MAXN];int anc[MAXN][18];int ans[MAXN];map<int, int>M; int M_point, to[MAXN << 3];struct Event {    int type, num;    int time, l, r, w;} ev[MAXN << 3];namespace segment_tree{    const int MAXS = MAXN * 250;    int seg_tot = 0, rot[MAXN], sz[MAXS], ch[MAXS][2];    int should_remove[250], should_add[250];    void build(int &now, int l, int r)    {        now = ++seg_tot;        if (l == r) return;        int mid = (l + r) >> 1;        build(ch[now][0], l, mid);        build(ch[now][1], mid + 1, r);    }    void initialize()    {        build(rot[0], 1, M_point);        FOR(i, 1, n) rot[i] = rot[0];    }    void push(int &now, int l, int r, int x, int modi)    {        int tmp = now;         now = ++seg_tot;        sz[now] = sz[tmp];        ch[now][0] = ch[tmp][0];        ch[now][1] = ch[tmp][1];        sz[now] += modi;        if (l == r) return;        int mid = (l + r) >> 1;        if (x <= mid) push(ch[now][0], l, mid, x, modi);        else push(ch[now][1], mid + 1, r, x, modi);    }    void getdown(int &ret, bool t)    {        ret = 0;        FOR(i, 1, should_add[0]) ret += sz[ch[should_add[i]][t]];        FOR(i, 1, should_remove[0]) ret -= sz[ch[should_remove[i]][t]];    }    void godown(bool t)    {        FOR(i, 1, should_add[0]) should_add[i] = ch[should_add[i]][t];        FOR(i, 1, should_remove[0]) should_remove[i] = ch[should_remove[i]][t];    }    void _query(int w, int l, int r, int num)    {        if (l == r) {            ans[num] = l;            return;        }        int ldata; getdown(ldata, 0);        int mid = (l + r) >> 1;        if (ldata >= w) {godown(0); _query(w, l, mid, num);}        else {godown(1); _query(w - ldata, mid + 1, r, num);}    }    void insert(int l, int r, int w)    {        if (l > r) {            debug("WA");        } //This line is for gdb        while (l <= n) {            push(rot[l], 1, M_point, w, 1);            l += l & -l;        }        r++;        while (r <= n) {            push(rot[r], 1, M_point, w, -1);            r += r & -r;        }    }    void remove(int l, int r, int w)    {        while (l <= n) {            push(rot[l], 1, M_point, w, -1);            l += l & -l;        }        r++;        while (r <= n) {            push(rot[r], 1, M_point, w, 1);            r += r & -r;        }    }    void query(int l, int r, int w, int num)    {        l--;        should_remove[0] = should_add[0] = 0;        while (l) {            should_remove[++should_remove[0]] = rot[l];            l -= l & -l;        }        while (r) {            should_add[++should_add[0]] = rot[r];            r -= r & -r;        }        _query(w, 1, M_point, num);    }}int lca(int x, int y){    if (depth[x] < depth[y]) x ^= y ^= x ^= y;    if (depth[x] != depth[y]) {        int delta = depth[x] - depth[y];        DNF(i, 17, 0) if (delta >> i & 1) x = anc[x][i];    }    if (x == y) return x;    DNF(i, 17, 0) if (anc[x][i] && anc[y][i] && anc[x][i] != anc[y][i]) {        x = anc[x][i]; y = anc[y][i];    }    return anc[x][0];}void work(int now){    if (ev[now].type == appear) {        segment_tree::insert(ev[now].l, ev[now].r, ev[now].w);    }    else if (ev[now].type == disappear) {        segment_tree::remove(ev[now].l, ev[now].r, ev[now].w);    }    else {        segment_tree::query(1, ev[now].l, ev[now].w, ev[now].num);    }}bool cmp(const Event &a, const Event &b){    return a.time < b.time || a.time == b.time && a.type < b.type;}bool cmp2(const Event &a, const Event &b){    return a.w < b.w;}void add(int x, int y){    nxt[cnt] = head[x]; data[cnt] = y; head[x] = cnt++;    nxt[cnt] = head[y]; data[cnt] = x; head[y] = cnt++;}void dfs(int now, int pa){    depth[now] = depth[pa] + 1;    anc[now][0] = pa;    for (int i = 1; anc[now][i - 1]; i++)        anc[now][i] = anc[anc[now][i - 1]][i - 1];    dfn[now] = ++INDEX;    for (int i = head[now]; i != -1; i = nxt[i]) {        if (data[i] != pa) dfs(data[i], now);    }    ed[now] = INDEX;}int find_fa(int now, int depth){    FOR(i, 0, 17) if (depth >> i & 1) now = anc[now][i];    return now;}int main(){    freopen("fruit.in", "r", stdin);    freopen("fruit.out", "w", stdout);    memset(head, -1, sizeof head);    in(n); in(p); in(q);    FOR(i, 1, n - 1) {        int u, v; in(u); in(v); add(u, v);    }    dfs(1, 0);    FOR(i, 1, p) {        int u, v, w;        in(u); in(v); in(w);        int anc = lca(u, v);        if (dfn[u] > dfn[v]) u ^= v ^= u ^= v;        if (u == anc) {            int nodes = find_fa(v, depth[v] - depth[u] - 1);            int ss = dfn[nodes], tt = ed[nodes];            if (ss != 1) {                ev[++ev_tot].time = 1;                ev[ev_tot].l = dfn[v]; ev[ev_tot].r = ed[v];                ev[ev_tot].type = appear; ev[ev_tot].w = w;                ev[++ev_tot].time = ss - 1;                ev[ev_tot].l = dfn[v]; ev[ev_tot].r = ed[v];                ev[ev_tot].type = disappear; ev[ev_tot].w = w;            }            if (tt != n) {                ev[++ev_tot].time = dfn[v];                ev[ev_tot].l = tt + 1; ev[ev_tot].r = n;                ev[ev_tot].type = appear; ev[ev_tot].w = w;                ev[++ev_tot].time = ed[v];                ev[ev_tot].l = tt + 1; ev[ev_tot].r = n;                ev[ev_tot].type = disappear; ev[ev_tot].w = w;            }        }        else {            ev[++ev_tot].time = dfn[u]; ev[ev_tot].l = dfn[v];            ev[ev_tot].r = ed[v]; ev[ev_tot].type = appear; ev[ev_tot].w = w;            ev[++ev_tot].time = ed[u]; ev[ev_tot].l = dfn[v];            ev[ev_tot].r = ed[v]; ev[ev_tot].type = disappear; ev[ev_tot].w = w;        }    }    std::sort(ev + 1, ev + ev_tot + 1, cmp2);    FOR(i, 1, ev_tot) {        if (i != 1 && ev[i].w == ev[i - 1].w) continue;        M[ev[i].w] = ++M_point;        to[M_point] = ev[i].w;    }    FOR(i, 1, ev_tot) {        ev[i].w = M[ev[i].w];    }    FOR(i, 1, q) {        int u, v, w;        in(u); in(v); in(w);        if (dfn[u] > dfn[v]) u ^= v ^= u ^= v;        ev[++ev_tot].time = dfn[u];        ev[ev_tot].l = dfn[v]; ev[ev_tot].w = w;        ev[ev_tot].num = i; ev[ev_tot].type = query;    }    std::sort(ev + 1, ev + ev_tot + 1, cmp);    segment_tree::initialize();    FOR(i, 1, ev_tot) work(i);    FOR(i, 1, q) printf("%d\n", to[ans[i]]);    return 0;}

Day1 T3 菜肴制作


思路

按照逆序拓扑排序,然后构造字典序最大的解。


代码

#include <bits/stdc++.h>using std::priority_queue;typedef long long LL;#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)template <typename Tp> void in(Tp &x) {    char ch = getchar(); x = 0;    while (ch < '0' || ch > '9') ch = getchar();    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();}template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}const int MAXN = 100010;int T, n, m, chose, du[MAXN], order[MAXN];int cnt, head[MAXN], data[MAXN << 1], nxt[MAXN << 1];priority_queue<int>q;void add(int x, int y){    nxt[cnt] = head[x]; data[cnt] = y; head[x] = cnt++;    nxt[cnt] = head[y]; data[cnt] = x; head[y] = cnt++;}int main(){    freopen("dishes.in", "r", stdin);    freopen("dishes.out", "w", stdout);    in(T);    while (T--) {        memset(du, 0, sizeof du);        memset(head, -1, sizeof head); cnt = 0;        in(n); in(m); chose = n;        FOR(i, 1, m) {            int x, y; in(x); in(y); add(y, x);            du[x]++;        }        FOR(i, 1, n) {            if (!du[i]) q.push(i);        }        order[0] = 0;        while (!q.empty()) {            int now = q.top(); q.pop(); chose--;            order[++order[0]] = now;            for (int i = head[now]; i != -1; i = nxt[i]) {                du[data[i]]--;                if (!du[data[i]]) {                    q.push(data[i]);                }            }        }        if (chose) puts("Impossible!");        else {            DNF(i, n, 1) printf("%d ", order[i]);            putchar(10);        }    }    return 0;}

Day2 T1 落忆枫音


思路

考虑没有环的情况。

每个点(除了 1 号点)任选一条入边,则构成一棵脉络树。

所有方案数为 ni=2degree[i]

但是有环,所以答案要减去它。

Syx 的路径。

remove=Sni=2[iS]degree[i]

这个可以dp……

f[i]ix 的路径求出的remove。

f[i]=j>if[j]/degree[i]


代码

#include <bits/stdc++.h>using std::queue;typedef long long LL;#define FOR(i, a, b) for (LL i = (a), i##_END_ = (b); i <= i##_END_; i++)#define DNF(i, a, b) for (LL i = (a), i##_END_ = (b); i >= i##_END_; i--)template <typename Tp> void in(Tp &x) {    char ch = getchar(); x = 0;    while (ch < '0' || ch > '9') ch = getchar();    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();}template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}const LL MOD = 1000000007;const LL MAXN = 200010;LL n, m, x, y, ans = 1;LL f[MAXN], du[MAXN], rdu[MAXN];LL head[MAXN], data[MAXN], nxt[MAXN], cnt;LL head1[MAXN], data1[MAXN], nxt1[MAXN], cnt1;queue<LL>q;void add(LL x, LL y){    nxt[cnt] = head[x]; data[cnt] = y; head[x] = cnt++;}void add2(LL x, LL y){    nxt1[cnt1] = head1[x]; data1[cnt1] = y; head1[x] = cnt1++;}LL power(LL x, LL y){    LL ret = 1;    while (y) {        if (y & 1) ret = 1ll * ret * x % MOD;        x = 1ll * x * x % MOD;        y >>= 1;    }    return ret;}int main(){    freopen("maple.in", "r", stdin);    freopen("maple.out", "w", stdout);    memset(head, -1, sizeof head);    memset(head1, -1, sizeof head1);    in(n); in(m); in(x); in(y);    FOR(i, 1, m) {        LL u, v;        in(u); in(v); add(u, v); add2(v, u);        du[v]++; rdu[v]++;    }    FOR(i, 2, n)        if (i != y)            ans = 1ll * ans * du[i] % MOD;    if (x == y || y == 1) {        if (y != 1)            ans = 1ll * ans * du[x] % MOD;        printf("%lld\n", ans);        return 0;    }    LL tmp = ans;    ans = 1ll * ans * (du[y] + 1) % MOD;    FOR(i, 1, n) {        if (!du[i]) q.push(i);    }    while (!q.empty()) {        LL now = q.front(); q.pop();        if (now == y) {            f[now] = tmp;        }        else {            f[now] = 0;            for (LL i = head1[now]; i != -1; i = nxt1[i]) {                f[now] = (f[now] + f[data1[i]]) % MOD;            }            f[now] = 1ll * f[now] * power(rdu[now], MOD - 2) % MOD;        }        for (LL i = head[now]; i != -1; i = nxt[i]) {            du[data[i]]--;            if (!du[data[i]]) {                q.push(data[i]);            }        }    }    printf("%lld\n", (ans - f[x] + MOD) % MOD);    return 0;}

Day2 T2


这是一道动态点分治的题目。但因为我还没有搞这个专题所以暂且跳过。


Day2 T3


思路

这个题目还是很容易的。

因为所有的 x 互不相同, 所以可以把大小关系看作是图论中的边。

那么构成的就是一座森林(把相等的点用并差集缩起来)。

然后就可以树形dp辣!

因为有等于,所以我们不知道每棵子树中有多少“块”元素。

我们把相等的元素看成一块,那么一棵子树就是若干个块的大小关系。

我们设第 i 个点的子树中有 j 个块的方案数为 f[i][j]

则两棵子树合并的结果 g[i]

g[i]=j=1nk=1n[max(j,k)ij+k]f[son1][j]f[son2][j](ij)(jj+ki)


代码

#include <bits/stdc++.h>typedef long long LL;#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)template <typename Tp> void in(Tp &x) {    char ch = getchar(); x = 0;    while (ch < '0' || ch > '9') ch = getchar();    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();}template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}const int MAXN = 110;const int MOD = 1000000007;struct Edge {    int l, r;} edge[MAXN];int edge_lenth;int n, m;char compare[10];int du[MAXN], f[MAXN][MAXN];int head[MAXN], nxt[MAXN], data[MAXN], cnt;int fa[MAXN];int find(int x){    int tmp = x, pre;    while (tmp != fa[tmp]) tmp = fa[tmp];    while (x != tmp) {        pre = fa[x]; fa[x] = tmp; x = pre;    }    return tmp;}void merge(int x, int y){    int fx = find(x), fy = find(y);    if (fx != fy) fa[fx] = fy;}int g[MAXN], jie[MAXN], ni[MAXN];int C(int x, int y){    if (!y) return 1;    if (x < y) return 0;    return 1ll * jie[x] * ni[y] % MOD * ni[x - y] % MOD;}void dfs(int now){    for (int i = head[now]; i != -1; i = nxt[i]) dfs(data[i]);    memset(g, 0, sizeof g);    if (head[now] != -1)        FOR(k, 1, n) g[k] = f[data[head[now]]][k];    if (head[now] != -1)         for (int i = nxt[head[now]]; i != -1; i = nxt[i]) {            memset(f[now], 0, sizeof f[now]);            FOR(k, 1, n) FOR(l, 1, n) FOR(j, Max(k, l), Min(n, k + l)) {                f[now][j] =    (                    f[now][j] +                    1ll * g[k] % MOD * f[data[i]][l] % MOD *                    C(j, k) % MOD * C(k, k + l - j) % MOD                ) % MOD;            }            FOR(k, 1, n) g[k] = f[now][k];        }    if (head[now] == -1) f[now][1] = 1;    else FOR(i, 1, n) f[now][i] = g[i - 1];}int power(int x, int y){    int ret = 1;    while (y) {        if (y & 1) ret = 1ll * ret * x % MOD;        x = 1ll * x * x % MOD;        y >>= 1;    }    return ret;}void add(int x, int y){    nxt[cnt] = head[x]; data[cnt] = y; head[x] = cnt++;}int main(){    freopen("pairwise.in", "r", stdin);    freopen("pairwise.out", "w", stdout);    memset(head, -1, sizeof head);    jie[0] = 1;    FOR(i, 1, 100) jie[i] = 1ll * jie[i - 1] * i % MOD;    ni[100] = power(jie[100], MOD - 2);    DNF(i, 99, 1) ni[i] = 1ll * ni[i + 1] * (i + 1) % MOD;    ni[0] = 1;    in(n); in(m);    FOR(i, 1, n) fa[i] = i;    FOR(i, 1, m) {        int x, y;        in(x); scanf("%s", compare); in(y);        if (compare[0] == '<') {            edge[++edge_lenth].l = x; edge[edge_lenth].r = y;        }        else merge(x, y);    }    int ans = 0;    FOR(i, 1, edge_lenth) {        int fx = find(edge[i].l);        int fy = find(edge[i].r);        if (fx == fy) {            puts("0");            return 0;        }        add(fx, fy); du[fy]++;    }    int rot = 0;    FOR(i, 1, n) {        if (!du[find(i)]) rot = find(i);    }    if (!rot) {        puts("0");        return 0;    }    dfs(rot);    FOR(i, 1, n) ans = (ans + f[rot][i]) % MOD;    printf("%d\n", ans);    return 0;}
原创粉丝点击