bzoj刷题记录4.25-5.1
1823: [JSOI2010]满汉全席
2-SAT裸题: a∨b=¬a→b=¬b→a ,然后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
关,吃掉的情况为
#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
个题正确的期望是:
#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) ,每个元素的贡献为:
注意: 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: 绿豆蛙的归宿
例如有 1→2,2→3,1→3,2→4,4→3 ,那么 1→3 的公式为:
令 dpi 为 1→i 所有路径上点出度倒数乘积乘上路径长的和, 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
门课,能换
正确的思路应该是
f[i][j][0/1]
表示前
i
门课,能换
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
获得
也就是至少发动一次的概率(即一次也不发动的反面)。而在哪一次发动则对于这个卡牌的贡献是等价的,我们已经通过计算 f[i][k] 将每个卡牌独立,也就消除了哪一次发动对贡献的影响。然后只要列 f[i][k] 的方程:
#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
,结果向量就是:
然后就完了。
脑残的我把
E−A
看成
−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;
}