COCI20122013 Contest#5 F

COCI20122013 Contest#5 F

不知道题解在写什么.jpg

Part1 : Naive的dp

(dp_{i,a,b,j})表示当前时刻(i),两队比分为(a,b),球在(j)手上的概率

转移非常简单就不说了,单次转移为(O(n)),复杂度为(O(n^2r^2T))

在优秀卡常+O2下跑进700ms

优化的话:
1.float

2.分小块加速

3.循环展开

#include<bits/stdc++.h>
using namespace std;
#pragma GCC optimize(2)
typedef float db;
#define reg register
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
template <class T> inline void cmin(T &a,const T &b){ ((a>b)&&(a=b)); }
template <class T> inline void cmax(T &a,const T &b){ ((a<b)&&(a=b)); }

char IO;
template <class T=int> T rd(){
	T s=0; int f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int INF=1e9+10;
const db eps=1e-12;

int n,R,T;
db dp[510][11][11][210],p[410],sz[410],ans[11][11];
struct Edge{
	int to,nxt;
} e[80000];
int head[410],ecnt;
void AddEdge(int u,int v) {
	e[++ecnt]=(Edge){v,head[u]};
	head[u]=ecnt;
}
int E[410][410],G[410][410]; 
const int D=5;
db tmp[210][1<<D];

int main(){
	n=rd(),R=rd(),T=rd();
	int m=(n*2+D-1)/D;
	rep(i,1,n*2) {
		scanf("%f",&p[i]);
		int e=rd(),f=rd();
		sz[i]=e+f+1;
		rep(j,1,e) {
			int x=rd();
			if(i>n) x+=n;
			E[i][x]=1;
		}
		rep(j,1,f) {
			int x=rd();
			if(i<=n) x+=n;
			E[i][x]=1;
		}
		rep(j,1,m) {
			int f=(j-1)*D+1;
			rep(k,0,D-1) G[i][j]|=E[i][f+k]<<k;
			if(G[i][j]) AddEdge(i,j);
		}
	}
	dp[0][0][0][1]=1;
	for(reg int i=0;i<=T;++i) {
		for(reg int a=0;a<=R;++a) {
			for(reg int b=0;b<=R;++b) {
				for(reg int j=1;j<=n*2;++j) if(dp[i][a][b][j]>eps) {
					if(a==R || b==R || i==T) { ans[a][b]+=dp[i][a][b][j]; continue; }
					db t=dp[i][a][b][j]/sz[j];
					for(reg int k=1;k<=m;k+=4) {
						tmp[k][G[j][k]]+=t;
						tmp[k+1][G[j][k+1]]+=t;
						tmp[k+2][G[j][k+2]]+=t;
						tmp[k+3][G[j][k+3]]+=t;
					}
					if(j<=n) {
						dp[i+1][a][b][n+1]+=t*(1-p[j]);
						dp[i+1][a+1][b][n+1]+=t*p[j];
					} else {
						dp[i+1][a][b][1]+=t*(1-p[j]);
						dp[i+1][a][b+1][1]+=t*p[j];
					}
				}
				for(reg int j=1;j<=m;++j) {
					int f=(j-1)*D+1;
					rep(k,1,(1<<D)-1) {
						(k&1) &&  (dp[i+1][a][b][f]+=tmp[j][k]);
						(k&2) &&  (dp[i+1][a][b][f+1]+=tmp[j][k]);
						(k&4) &&  (dp[i+1][a][b][f+2]+=tmp[j][k]);
						(k&8) &&  (dp[i+1][a][b][f+3]+=tmp[j][k]);
						(k&16) &&  (dp[i+1][a][b][f+4]+=tmp[j][k]);
						tmp[j][k]=0;
					}
				}
			}
		}
	}
	rep(i,0,R) rep(j,0,R) if(i!=R || j!=R) printf("%.10f
",ans[i][j]);
}

[ ]

Part2: 状态割裂

定义每个球进的时间为关键点,我们发现关键点的状态非常单一,只有两种

一个合法的转移序列可以被分为若干关键点的段以及最后一段到达(T)之后停止转移

考虑预处理两个关键点之间的转移概率

(g_{a,b,i})为当球在(a)队一号队员时,(i)次后(b)队进球的概率

可以枚举(a),类似上面的(dp),去掉比分的一维即可

预处理复杂度为(O(Tn^2))

然后(dp)时直接枚举两个关键点转移,令

(h_{i,a,b,j})时刻(i)比分为(a,b),球在(j)队一号队员手上的概率

转移分两种

1.枚举下一个在(T)以内的关键点转移

复杂度为(O(T))

2.考虑在(T)以内的时间不再出现进球了

需要预处理出当球在(i)队手上时,(j)次内出现进球的概率(s_{i,j}),这个直接由(g)数组累前缀和即可

(dp)关键点的复杂度为(O(T^2r^2))

大概比上面代码快4-5倍

#include<bits/stdc++.h>
using namespace std;
#pragma GCC optimize(2)
typedef float db;
#define reg register
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

char IO;
template <class T=int> T rd(){
    T s=0; int f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}

const int INF=1e9+10;
const db eps=1e-12;

int n,R,T;
struct Edge{
    int to,nxt;
} e[80000];
int head[410],ecnt;
void AddEdge(int u,int v) {
    e[++ecnt]=(Edge){v,head[u]};
    head[u]=ecnt;
}
db p[410],sz[410],ans[11][11],f[510][210],g[2][2][510],s[2][510];
// g[i][j][k] 在i拿球的情况下,j在第k次进球了
db h[510][11][11][2];

int main(){
    n=rd(),R=rd(),T=rd();
    rep(i,1,n*2) {
        scanf("%f",&p[i]);
        int e=rd(),f=rd();
        sz[i]=e+f+1;
        rep(j,1,e) {
            int x=rd();
            if(i>n) x+=n;
            AddEdge(i,x);
        }
        rep(j,1,f) {
            int x=rd();
            if(i<=n) x+=n;
            AddEdge(i,x);
        }
    }
    rep(d,0,1) {
        f[0][d*n+1]=1;
        rep(i,0,T) {
            rep(j,1,n*2) if(f[i][j]>eps) {
                db t=f[i][j]/sz[j];
                for(reg int k=head[j];k;k=e[k].nxt) f[i+1][e[k].to]+=t;
                f[i+1][j>n?1:n+1]+=t*(1-p[j]);
                g[d][j>n][i+1]+=t*p[j];
                f[i][j]=0;
            }
        }
        rep(i,0,T) {
            s[d][i]=g[d][0][i]+g[d][1][i];
            if(i) s[d][i]+=s[d][i-1];
        }
    }
    h[0][0][0][0]=1;
    rep(i,0,T) {
        rep(a,0,R) rep(b,0,R) rep(j,0,1) if(h[i][a][b][j]>eps) {
            if(a==R || b==R || i==T){ ans[a][b]+=h[i][a][b][j]; continue; }
            rep(k,1,T-i) {
                // 能在结束前产生一次进球
                h[i+k][a+1][b][1]+=h[i][a][b][j]*g[j][0][k];
                h[i+k][a][b+1][0]+=h[i][a][b][j]*g[j][1][k];
            }
            ans[a][b]+=h[i][a][b][j]*(1-s[j][T-i]);
        }
    }
    rep(i,0,R) rep(j,0,R) if(i<R || j<R) printf("%.10f
",ans[i][j]);
}



原文地址:https://www.cnblogs.com/chasedeath/p/13591409.html