bzoj刷题记录4.25-5.1

bzoj刷题记录4.25-5.1

1823: [JSOI2010]满汉全席

2-SAT裸题: ab=¬ab=¬ba ,然后tarjan就行了。

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 505, MAXM = 10005;

struct node {
    int to, next;
} edge[MAXM];
int head[MAXN], top = 0;
void push(int i, int j)
{ edge[++top] = (node){j, head[i]}, head[i] = top; }

int n, m;

int neg(int nd)
{ return nd>n?nd-n:nd+n; }

int T;

int dfn[MAXN], low[MAXN], stk[MAXN], instk[MAXN], stop = 0, dftop = 0;

bool tarjan(int nd)
{
    dfn[nd] = low[nd] = ++dftop;
    instk[nd] = 1, stk[++stop] = nd;
    for (int i = head[nd]; i; i = edge[i].next) {
        int to = edge[i].to;
        if (!dfn[to]) {
            if (!tarjan(to)) return 0;
            low[nd] = min(low[nd], low[to]);
        }
        else if (instk[to]) low[nd] = min(low[nd], dfn[to]);
    }
    if (dfn[nd] == low[nd]) {
        int now = 0;
        do {
            now = stk[stop--], instk[now] = 0;
            if (now == neg(nd)) return 0;
        } while (now != nd); 
    }
    return 1;
}

bool fail()
{
    memset(dfn, 0, sizeof dfn);
    for (int i = 1; i <= n*2; i++)
        if (!dfn[i])
            if (!tarjan(i)) return 1;
    return 0;
}

int main()
{
    scanf("%d", &T);
    while (T--) {
        top = 0, memset(head, 0, sizeof head);
        dftop = 0, stop = 0;
        memset(instk, 0, sizeof instk);
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= m; i++) {
            char t1, t2; int n1, n2;
            scanf("\n%c%d %c%d", &t1, &n1, &t2, &n2);
            if (t1 == 'm') n1 = neg(n1);
            if (t2 == 'm') n2 = neg(n2);
            push(neg(n1), n2), push(neg(n2), n1);
        }
        if (fail()) puts("BAD");
        else puts("GOOD");
    }
    return 0;
}

bzoj2756: [SCOI2012]奇怪的游戏

网络流。http://hzwer.com/5992.html

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 50*51, MAXM = 50*50*50;
struct node {
    int to, next;
    long long flow;
    int neg;
} edge[MAXM];
int head[MAXN], top = 0;
void push(int i, int j, long long f)
{
    ++top, edge[top] = (node){j, head[i], f, top+1}, head[i] = top;
    ++top, edge[top] = (node){i, head[j], 0, top-1}, head[j] = top;
}

int S = 2531, T = 2532;
long long inf = 233333333333ll;

int vis[MAXN], bfstime = 0, lev[MAXN];
queue<int> que;

bool bfs()
{
    vis[S] = ++bfstime, lev[S] = 0, que.push(S);
    while (!que.empty()) {
        int f = que.front(); que.pop();
        for (int i = head[f]; i; i = edge[i].next) {
            int to = edge[i].to;
            long long fl = edge[i].flow;
            if (!fl || vis[to] == bfstime) continue;
            lev[to] = lev[f] + 1, vis[to] = bfstime, que.push(to);
        }
    }
    return vis[T] == bfstime;
}

long long dfs(int nd, long long mf = inf)
{
    if (nd == T || !mf) return mf;
    long long t, ans = 0;
    for (int i = head[nd]; i; i = edge[i].next) {
        int to = edge[i].to; long long f = edge[i].flow;
        if (!f || lev[to] != lev[nd]+1) continue;
        t = dfs(to, min(mf, f));
        ans += t, mf -= t;
        edge[i].flow -= t, edge[edge[i].neg].flow += t;
    }
    if (mf) lev[nd] = -1;
    return ans;
}

long long dinic()
{
    long long ans = 0;
    while (bfs()) ans += dfs(S);
    return ans;
}

long long chess_board[55][55];
int n, m, Tp;
long long x[2] = {0,0}, sx[2] = {0,0};

inline int number(int i, int j)
{ return (i-1)*m+j;}

int dx[] = {0,1,0,-1}, dy[] = {1,0,-1,0};

bool judge(long long z)
{
    bfstime = 0, top = 0, memset(head, 0, sizeof head);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) {
            if (z-chess_board[i][j] < 0) return 0;
            if ((i+j)&1) push(S, number(i, j), z-chess_board[i][j]);
            else push(number(i, j), T, z-chess_board[i][j]);
        }
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) {
            if (!((i+j)&1)) continue;
            for (int k = 0; k < 4; k++) {
                int nx = i+dx[k], ny = j+dy[k];
                if (nx >= 1 && nx <= n && ny >= 1 && ny <= m)
                    push(number(i, j), number(nx, ny), inf);
            }
        }
    long long fl = dinic();
    return fl == z*x[0]-sx[0];
}

int main()
{
    scanf("%d", &Tp);
    while (Tp--) {
        scanf("%d%d", &n, &m);
        x[0] = x[1] = sx[0] = sx[1] = 0;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++) {
                scanf("%lld", &chess_board[i][j]);
                x[(i+j)&1]++, sx[(i+j)&1] += chess_board[i][j];
            }
        if (x[0] != x[1]) {
            if ((sx[0]-sx[1])%(x[0]-x[1]) != 0) puts("-1");
            else {
                long long val = (sx[0]-sx[1])/(x[0]-x[1]);
                if (judge(val)) printf("%lld\n", val*x[0]-sx[0]);
                else puts("-1");
            }
        } else {
            if (sx[0] != sx[1]) puts("-1");
            else {
                long long l = 0, r = inf, mid;
                while (l <= r) {
                    mid = (l+r)>>1;
                    if (!judge(mid)) l = mid+1;
                    else r = mid-1;
                }
                printf("%lld\n", l*x[0]-sx[0]);
            }
        }
    }
    return 0;
}

bzoj2809: [Apio2012]dispatching

第一反应是可并堆.
然而可并堆的空间会不会爆炸呢..后来发现是不会炸的..每个元素至多只入一次堆,删除一次,总复杂度还是 O(nlgn) 的。

结果写了可持久化权值线段树..离散化,找出dfn序用可持久化线段树维护区间内有值的点数和总和,然后二分就好,复杂度也是 O(nlgn) .

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 100005, lgn = 20;
int lc[MAXN*20], rc[MAXN*20], l[MAXN*20];
int r[MAXN*20], top = 0;
long long dat[MAXN*20], sum[MAXN*20], siz[MAXN*20], dx[MAXN];
int root[MAXN];
int n;
long long m;

inline void update(int nd)
{
    sum[nd] = sum[lc[nd]] + sum[rc[nd]] + dat[nd];
    if (l[nd] == r[nd]) siz[nd] = dat[nd] != 0;
    else siz[nd] = siz[lc[nd]] + siz[rc[nd]];
}

void build(int &nd, int opl, int opr)
{
    nd = ++top, l[nd] = opl, r[nd] = opr;
    if (opl < opr)
        build(lc[nd], opl, (opl+opr)/2), build(rc[nd], (opl+opr)/2+1, opr);
}

void push(int prev, int &nd, int pos)
{
    nd = ++top, l[nd] = l[prev], r[nd] = r[prev];
    if (l[prev] == r[prev]) dat[nd] = dx[pos]/(n+1), update(nd);
    else {
        int mid = (l[prev]+r[prev])/2;
        if (pos <= mid) push(lc[prev], lc[nd], pos), rc[nd] = rc[prev], update(nd);
        else push(rc[prev], rc[nd], pos), lc[nd] = lc[prev], update(nd);
    }
}

int answer(int lpos, int rpos, long long sm)
{
    lpos--;
    int i = 1, j = n, mid, lr = root[lpos], rr = root[rpos];
    int ans = 0;
    while (i < j) {
        mid = (i+j)>>1;
        int lp = lc[lr], rp = lc[rr];
        if (sum[rp]-sum[lp] >= sm) lr = lp, rr = rp, j = mid;
        else sm -= sum[rp]-sum[lp], ans += siz[rp]-siz[lp], lr = rc[lr], rr = rc[rr], i = mid+1;
    }
    if (sm >= sum[rr]-sum[lr]) ans += siz[rr]-siz[lr];
    return ans;
}

struct node {
    int to, next;
} edge[MAXN];
int head[MAXN], tp = 0;
void push(int i, int j)
{ edge[++tp] = (node){j, head[i]}, head[i] = tp; }

int fa[MAXN], in[MAXN], out[MAXN], dfn = 0;
long long money[MAXN], leader[MAXN];

int dx_num(long long num, int i)
{ return lower_bound(dx+1, dx+n+1, num*(n+1)+i)-dx; }

void dx_init()
{
    for (int i = 1; i <= n; i++) dx[i] = money[i]*(n+1)+i;
    sort(dx+1, dx+n+1);
}

void dfs(int nd)
{
    in[nd] = ++dfn;
    push(root[dfn-1], root[dfn], dx_num(money[nd], nd));
    for (int i = head[nd]; i; i = edge[i].next)
        dfs(edge[i].to);
    out[nd] = dfn;
}

long long query(int nd)
{ return leader[nd]*answer(in[nd], out[nd], m); }

int main()
{
    scanf("%d%lld", &n, &m);
    int master = 0;
    for (int i = 1; i <= n; i++)
        scanf("%d%lld%lld", &fa[i], &money[i], &leader[i]);
    build(root[0], 1, n);
    dx_init();
    for (int i = 1; i <= n; i++) {
        if (!fa[i]) master = i;
        else push(fa[i], i);
    }
    dfs(master);
    long long ans = 0;
    for (int i = 1; i <= n; i++)
        ans = max(ans, query(i));
    printf("%lld\n", ans);
    return 0;
}

bzoj3206: [Apio2013]道路费用

没过…
用国内数据和hzwer及几分其他std拍都拍过了,然而bzoj上还是WA…奇特的是我们拿那份Te数据都WA一个点且结果一样。

然而hzwer代码bzoj可过而这份不可过…

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int MAXN = 100005, MAXM = 300105;
int fa[MAXN];
inline int findf(int nd)
{ return fa[nd]?fa[nd]=findf(fa[nd]):nd; }
int gp[MAXN];
long long siz[MAXN];
inline int findgp(int nd)
{ return gp[nd]?gp[nd]=findgp(gp[nd]):nd; }
void link(int i, int j)
{
    int a = findgp(i), b = findgp(j);
    if (a != b)
        gp[a] = b, siz[b] += siz[a];
}

struct edge {
    int x, y;
    long long c;
    int on_eg;
    edge(){ on_eg = 1; }
    friend bool operator < (const edge &a, const edge &b)
    { return a.c < b.c; }
} edge[MAXM], knew[105], inp[MAXM];
int n, m, k;
const long long INF = 1234567891011ll;

struct node {
    int to, next;
    long long dis;
} graph[105];
int head[MAXN], top = 0;
int stk[50], t = 0;
inline void push(int i, int j, long long dis)
{ //printf("%d--%lld-->%d\n", i, dis, j);
 graph[++top] = (node){j, head[i], dis}, head[i] = top; }

void mst(int eg, int flag = 0, int p = 0)
{
    // cout << eg << endl;
    sort(edge+1, edge+eg+1);
    if (!p) memset(fa, 0, sizeof fa);
    else {
        for (int i = 1; i <= t; i++)
            fa[stk[i]] = 0;
    }
    for (int i = 1; i <= eg; i++) {
        int a = findf(findgp(edge[i].x)), b = findf(findgp(edge[i].y));
        if (a != b) {
            fa[a] = b;
            // printf("On MST --- %d, %d, %lld\n", a, b, edge[i].c);
            if (flag) {
                push(findgp(edge[i].x), findgp(edge[i].y), edge[i].c);
                push(findgp(edge[i].y), findgp(edge[i].x), edge[i].c);
            }
        } else {
            if (!p)edge[i].c = INF;
            edge[i].on_eg = 0;
        }
    }
}

void cut1()
{
    memcpy(edge, inp, sizeof inp);
    int tp = m;
    for (int i = 1; i <= k; i++)
        edge[++tp] = knew[i], edge[tp].c = -INF;
    mst(tp);
    for (int i = 1; i <= tp; i++)
        if (abs(edge[i].c) != INF)
            link(edge[i].x, edge[i].y);
}

void cut2()
{
    memcpy(edge, inp, sizeof inp);
    mst(m);
    int tp = m;
    m = 0;
    for (int i = 1; i <= tp; i++)
        if (edge[i].c != INF) {
            inp[++m] = edge[i], inp[m].on_eg = 1;
            // printf("Cut2 %d--%lld--%d\n", edge[i].x, edge[i].c, edge[i].y);
        }
}

int depth[MAXN], par[MAXN];
long long peo[MAXN];
long long lim[MAXN];

void dfs(int nd, int f)
{
    depth[nd] = depth[f]+1;
    par[nd] = f, lim[nd] = INF;
    peo[nd] = siz[nd];
    for (int i = head[nd]; i; i = graph[i].next) {
        int to = graph[i].to;
        if (to != f) dfs(to, nd), peo[nd] += peo[to];
    }
}

void push_lim(int x, int y, long long l)
{
    while (x != y) {
        if (depth[x] >= depth[y]) {
            lim[x] = min(lim[x], l);
            x = par[x];
        } else {
            lim[y] = min(lim[y], l);
            y = par[y];
        }
    }
}


long long deal(int S)
{
    memcpy(edge, inp, k*16*sizeof(int));
    top = 0;
    int tp = m;
    // cout << "---" <<edge[1].c << endl;
    for (int i = 1; i <= t; i++)
        head[stk[i]] = 0;
    for (int i = 1; i <= k; i++)
        if (S&(1<<(i-1))) {
            edge[++tp] = knew[i], edge[tp].c = -INF, edge[tp].on_eg = 1;
            if (findgp(knew[i].x) == findgp(knew[i].y)) return 0ll;
        }
    // for (int i = 1; i <= tp; i++)
        // if (edge[i].on_eg != 1)
            // throw;
    mst(tp, 1, 1);
    // printf("     tp = %d    \n", tp);
    dfs(findgp(1), 0);
    long long ans = 0;
    for (int i = 1; i <= tp; i++) {
        int x = findgp(edge[i].x), y = findgp(edge[i].y);
        if (edge[i].on_eg == 0) {
            push_lim(x, y, edge[i].c);
            // printf("Pushlim %d,%d -- %lld\n", x, y, edge[i].c);
            // edge[i].on_eg = 1;
        } //else printf("Lk %d -- %lld -- %d\n", x, edge[i].c, y);
    }
    for (int i = 1; i <= k; i++)
        if (S&(1<<(i-1))) {
            int x = findgp(knew[i].x), y = findgp(knew[i].y);
            if (depth[x] < depth[y]) swap(x, y);
            long long l = lim[x];
            ans += l*peo[x];
            // cout << x << " " << y << " " << l << " " << peo[x] << endl;
        }
    // cout << ans << endl;
    return ans;
}

int main()
{
    freopen("toll.in", "r", stdin);
    freopen("toll.out", "w", stdout);
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1; i <= m; i++)
        scanf("%d%d%lld", &inp[i].x, &inp[i].y, &inp[i].c), inp[i].on_eg = 1;
    for (int i = 1; i <= k; i++)
        scanf("%d%d", &knew[i].x, &knew[i].y);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &siz[i]);
    cut1();
    cut2();
    for (int i = 1; i <= n; i++)
        if (gp[i] == 0)
            stk[++t] = i;
    long long ans = 0;
    for (int i = 0; i < (1<<k); i++) {
        ans = max(ans, deal(i));
    }
    cout << ans << endl;
    return 0;
}

bzoj1912: [Apio2010]patrol 巡逻

第一反应是先找最长链,然后缩成一个点再找最长链,然而只有55分。
后来反应过来两个链相交会有损耗,于是看到了大爷题解中把环上边权设为-1再走最长链的方法..
APIO神题好多..知识水平不超过NOIP但是思维难度很大..

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 100005;
struct node {
    int to, next, dis, neg;
} edge[MAXN*2];
int head[MAXN], top = 0;
void push(int i, int j)
{
    top++, edge[top] = (node){j, head[i], 1, top+1}, head[i] = top;
    top++, edge[top] = (node){i, head[j], 1, top-1}, head[j] = top;
}
int n, K;
int gp[MAXN], siz[MAXN];

int stk[MAXN], stop = 0;
int ans = 0;
int depth[MAXN], maxl[MAXN], secl[MAXN], maxps[MAXN], secps[MAXN];
int fa[MAXN];
void dfs(int nd, int f = 0)
{
    maxl[nd] = secl[nd] = maxps[nd] = secps[nd] = 0;
    for (int i = head[nd]; i; i = edge[i].next) {
        int to = edge[i].to;
        if (to == f) continue;
        fa[to] = i;
        depth[to] = depth[nd]+1, dfs(to, nd);
        if (maxl[to]+edge[i].dis >= maxl[nd]) secl[nd] = maxl[nd], secps[nd] = maxps[nd], maxl[nd] = maxl[to]+edge[i].dis, maxps[nd] = to;
        else if (maxl[to]+edge[i].dis > secl[nd]) secl[nd] = maxl[to]+edge[i].dis, secps[nd] = to;
    }
}

void deal()
{
    memset(depth, 0, sizeof depth);
    dfs(1, 0);
    //puts("---");
    int len = 0, nd;
    for (int i = 1; i <= n; i++)
        if (maxl[i] + secl[i] > len) len = maxl[i]+secl[i], nd = i;
    //puts("---");
    ans -= len*2, ans += len+1;
    for (int i = maxps[nd]; i; i = maxps[i])
        edge[fa[i]].dis = -1, edge[edge[fa[i]].neg].dis = -1;
    for (int i = secps[nd]; i; i = maxps[i])
        edge[fa[i]].dis = -1, edge[edge[fa[i]].neg].dis = -1;
    //puts("---");
}

int main()
{
    scanf("%d%d", &n, &K);
    memset(gp, 0, sizeof gp);
    for (int i = 1; i <= n; i++) siz[i] = 1;
    for (int i = 1; i < n; i++) {
        int u, v; scanf("%d%d", &u, &v);
        push(u, v);
    }
    ans = (n-1)*2;
    deal();
    if (K == 2) deal();
    cout << ans << endl;
    return 0;
}

bzoj1076: [SCOI2008]奖励关

学习一个期望dp..
f(k,S) 为第 k 关,吃掉的情况为S之后的期望得分。如果 prev[i]S,f(k,S)=max{f(k+1,S),f(k+1,Si)+v[i]} ,否咋 f(k,S)=f(k+1,S)

#include <bits/stdc++.h>
using namespace std;

double dp[105][(1<<16)+1];
int k, n;
int rp[105], st[105];

int main()
{
    scanf("%d%d", &k, &n);
    for (int i = 1; i <= n; i++) {
        int t, u;
        scanf("%d", &rp[i]);
        st[i] = 0;
        while (scanf("%d", &u), u != 0)
            st[i] |= (1<<(u-1));
    }
    for (int i = 0; i < (1<<n); i++) dp[k+1][i] = 0;
    for (int i = k; i >= 1; i--) {
        for (int j = 0; j < (1<<n); j++) {
            dp[i][j] = 0;
            for (int p = 1; p <= n; p++) {
                if ((j&st[p])==st[p])
                    dp[i][j] += max(dp[i+1][j], dp[i+1][j|(1<<(p-1))]+rp[p]*1.0);
                else
                    dp[i][j] += dp[i+1][j];
            }
            dp[i][j] /= n;
        }
    }
    printf("%.6f\n", dp[1][0]);
    return 0;
}

bzoj2134: 单选错位

分别计算贡献,令 a0=an ,则第 i 个题正确的期望是:p(i)=min{ai,ai1}/(aiai1),总和即为所求。

#include <bits/stdc++.h>
using namespace std;

int n, A, B, C;
int a[10000005];
int main()
{
    scanf("%d%d%d%d%d", &n, &A, &B, &C, a+1);
    for (int i=2;i<=n;i++) a[i] = ((long long)a[i-1] * A + B) % 100000001;
    for (int i=1;i<=n;i++) a[i] = a[i] % C + 1;
    // for (int i = 1; i <= n; i++) cout << a[i] << " "; puts("");
    double now = 0;
    a[0] = a[n];
    for (int i = 1; i <= n; i++) {
        now = min(a[i],a[i-1])*1.0/a[i]/a[i-1]+now;
    }
    printf("%.3f\n", now);
    return 0;
}

bzoj4318: OSU!

大力递推可以 O(n3) 并优化至 O(n2)

#include <bits/stdc++.h>
using namespace std;

double p[100005];
int n;
double dp[100005];

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%lf", &p[i]);
    for (int i = 1; i <= n; i++) {
        dp[i] = 0;
        for (int k = 0; k <= i; k++) {
            if (k+1 <= i) {
                double tmp = 1-p[k];
                for (int j = k+1; j <= i; j++)
                    tmp *= p[j];
                dp[i] += tmp*((i-k)*(i-k)*(i-k)+dp[k-1]);
            } else dp[i] += dp[k-1]*(1-p[k]);
        }
    }
    cout << dp[n] << endl;
    return 0;
}

然而这个做法没有很好地利用期望的线性性质,即 E(aX+bY)=aE(X)+bE(Y) 。考虑这个元素之前期望的连续1长度为 E(x) ,连续1长度的平方为 E(x2) ,每个元素的贡献为:

(x+1)3x3=3x2+3x+1=3E(x2)+3E(x)+1

注意: E(x2)E2(x)

#include <bits/stdc++.h>
using namespace std;

double p[100005];
int n;

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%lf", &p[i]);
    double x, x2, ans; x = x2 = 0, ans = 0;
    for (int i = 1; i <= n; i++) {
        ans += (x2*3+3*x+1)*p[i];
        x2 = (x2+2*x+1)*p[i];
        x = (x+1)*p[i];
    }
    printf("%.1f\n", ans);
    return 0;
}

bzoj3036: 绿豆蛙的归宿

例如有 12,23,13,24,43 ,那么 13 的公式为:

1d1c13+1d11d2(c12+c23)+1d11d21d4(c12+c24+c43)

dpi 1i 所有路径上点出度倒数乘积乘上路径长的和, fi 为路径上所有点出度倒数乘积的和,然后发现有递推关系:

f[to] += f[nd]/d[tp];
dp[to] += dp[nd]/cd[tp]+c(nd,to)*f[nd]/cd[nd];
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 100005;
struct node {
    int to, next, dis;
} edge[MAXN*2];
int head[MAXN], top = 0, rd[MAXN], cd[MAXN];
void push(int i, int j, int d)
{ edge[++top] = (node){j, head[i], d}, head[i] = top, rd[j]++, cd[i]++; }

int stk[MAXN], stop = 0;
int n, m;
double dp[MAXN], f[MAXN];

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++) {
        int u, v, d;
        scanf("%d%d%d", &u, &v, &d);
        push(u, v, d);
    }
    stk[++stop] = 1;
    dp[1] = 0, f[1] = 1;
    while (stop) {
        int tp = stk[stop--];
        for (int i = head[tp]; i; i = edge[i].next) {
            int to = edge[i].to;
            f[to] += f[tp]/cd[tp], dp[to] += dp[tp]/cd[tp]+edge[i].dis*f[tp]/cd[tp];
            rd[to]--;
            if (rd[to] == 0) stk[++stop] = to;
        }
    }
    printf("%.2f\n", dp[n]);
    return 0;
}

bzoj4720: [Noip2016]换教室

终于来填坑了…

考场上设计的状态 f[i][j][0/1] 表示前 i 门课,能换j次,这次在哪个教室。然而无论怎么调都有后效性,只能记录路径上概率乘积变爆搜了..

正确的思路应该是 f[i][j][0/1] 表示前 i 门课,能换j次,这次提不提出申请,那么转移方程就是:

f[i][j][0]=max{f[i1][j][0]+dis(ci1,ci),(1p[i1])(f[i1][j][1]+dis(ci1,ci))+p[i1](f[i1][j][1]+dis(di1,ci))}

i,j,1 同理枚举四种可能情况即可。

#include <bits/stdc++.h>
using namespace std;

int n, m, v, e;
int g[505][505];
int c[2005][2];
double k[2005];
double f[2005][2005][2];
int main()
{
    scanf("%d%d%d%d", &n, &m, &v, &e);
    for (int i = 1; i <= n; i++) scanf("%d", &c[i][0]);
    for (int i = 1; i <= n; i++) scanf("%d", &c[i][1]);
    for (int i = 1; i <= n; i++) scanf("%lf", &k[i]);
    memset(g, 127/3, sizeof g);
    for (int i = 1; i <= v; i++) g[i][i] = 0;
    for (int i = 1; i <= e; i++) {
        int u, v, d; scanf("%d%d%d", &u, &v, &d);
        g[u][v] = g[v][u] = min(g[u][v], d);
    }
    for (int k = 1; k <= v; k++)
        for (int i = 1; i <= v; i++)
            for (int j = 1; j <= v; j++)
                g[j][i] = g[i][j] = min(g[i][j], g[i][k]+g[k][j]);
    for (int i = 0; i <= m; i++) f[1][i][0] = f[1][i][1] = 1e10;
    f[1][0][0] = f[1][1][1] = f[1][1][0] = 0;
    for (int i = 2; i <= n; i++) {
        for (register int j = 0; j <= m; j++) {
            f[i][j][0] = f[i][j][1] = 1e10;
            f[i][j][0] = min(f[i-1][j][0]+g[c[i-1][0]][c[i][0]],
                f[i-1][j][1]+k[i-1]*g[c[i-1][1]][c[i][0]]+(1-k[i-1])*g[c[i-1][0]][c[i][0]]);
            if (j != 0) {
                f[i][j][1] = min(f[i-1][j-1][0]+k[i]*g[c[i-1][0]][c[i][1]]+(1-k[i])*g[c[i-1][0]][c[i][0]],
                    f[i-1][j-1][1]+k[i]*k[i-1]*g[c[i-1][1]][c[i][1]]+(1-k[i-1])*(1-k[i])*g[c[i-1][0]][c[i][0]]
                    +k[i]*(1-k[i-1])*g[c[i-1][0]][c[i][1]]+k[i-1]*(1-k[i])*g[c[i-1][1]][c[i][0]]);
                f[i][j][0] = min(f[i][j][0], f[i][j-1][0]);
                f[i][j][1] = min(f[i][j][1], f[i][j-1][1]);
            }
        }
    }
    double ans = f[n][0][0];
    for (int i = 1; i <= m; i++) ans = min(f[n][i][1], f[n][i][0]);
    printf("%.2f\n", ans);
    return 0;
}

bzoj4008: [HNOI2015]亚瑟王

huzecong dalao的神题…
直接dp会发现无论怎么设计状态都不可能消除后效性。
然后就比较神了,分别计算每个卡牌的贡献,如果我们知道了这个卡牌 i 获得k次发动机会的概率 f[i][k] ,那么其贡献就为:

kf[i][k]×(1(1p[i])k)×di

也就是至少发动一次的概率(即一次也不发动的反面)。而在哪一次发动则对于这个卡牌的贡献是等价的,我们已经通过计算 f[i][k] 将每个卡牌独立,也就消除了哪一次发动对贡献的影响。然后只要列 f[i][k] 的方程:

f[i][k]=f[i1][k]×(1p[i1])k+f[i1][k+1]×(1(1p[i1])k+1)

#include <bits/stdc++.h>
using namespace std;

int T, n, r;
double dp[225][140];
double p[225], d[225];

int main()
{
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &n, &r);
        double tmp = 1;
        for (int i = 1; i <= n; i++) scanf("%lf%lf", &p[i], &d[i]), tmp *= p[i];
        double ans = 0;
        memset(dp, 0, sizeof dp);
        dp[0][r] = 1;
        for (int i = 1; i <= n; i++)
            for (int j = 0; j <= r; j++) {
                dp[i][j] = dp[i-1][j]*pow(1-p[i-1], j)+dp[i-1][j+1]*(1-pow(1-p[i-1], j+1));
                ans += dp[i][j]*(1-pow(1-p[i], j))*d[i];
            }
        printf("%.10f\n", ans);
    }
    return 0;
}

bzoj3270: 博物馆

和《游走》那个题套路一样。
把一个有序对看成状态列转移矩阵 A ,结果向量就是:

λ=0Aλx0=x0IIA

然后就完了。
脑残的我把 EA 看成 A 大概需要治疗

#include <bits/stdc++.h>
using namespace std;

double A[405][805];
int g[25][25], n, m, u, v, a, b;
double p[25], eps = 1e-10;
inline int num(int i, int j)
{ return (i-1)*n+j; }

void inverse()
{
    for (int i = 1; i <= n*n; i++) A[i][i+n*n] = 1;
    for (int i = 1; i <= n*n; i++) {
        int k = i;
        while (abs(A[k][i]) <= eps) k++;
        swap(A[i], A[k]);
        for (int j = 1; j <= n*n; j++) {
            if (j == i) continue;
            double tmp = -A[j][i]/A[i][i];
            for (int k = 1; k <= 2*n*n; k++)
                A[j][k] += A[i][k]*tmp;//, printf("%f ",A[j][k]);
        }
    }
    for (int i = 1; i <= n*n; i++)
        for (int j = n*n+1; j <= n*n*2; j++)
            A[i][j] /= A[i][i];
    for (int i = 1; i <= n*n; i++)
        for (int j = 1; j <= n*n; j++)
            A[i][j] = A[i][j+n*n];
}

int main()
{
    scanf("%d%d%d%d", &n, &m, &a, &b);
    for (int i = 1; i <= m; i++)
        scanf("%d%d", &u, &v), g[u][v] = g[v][u] = 1, g[v][v]++, g[u][u]++;
    for (int i = 1; i <= n; i++) scanf("%lf", &p[i]);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            for (int k = 1; k <= n; k++)
                for (int l = 1; l <= n; l++) { // (i,j)-->(k,l)
                    if ((i == k || g[i][k]) && (g[j][l] || j==l)) {
                        double ti = i==k?p[i]:(1-p[i])*1.0/g[i][i];
                        double tj = j==l?p[j]:(1-p[j])*1.0/g[j][j];
                        A[num(k,l)][num(i,j)] = -ti*tj;
                    }
                    if (i == j)
                        A[num(k,l)][num(i,j)] = 0;
                }
    for (int i = 1; i <= n*n; i++) A[i][i] += 1;
    inverse();
    for (int i = 1; i <= n; i++)
        printf("%.6f ", A[num(i,i)][num(a,b)]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值