【HDU 3037】Saving Beans

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=3037
数据生成器:http://paste.ubuntu.com/22886753/

lucas定理 + 插板法

#include<iostream>
#include<cstdio>
#define LL long long
using namespace std;

const int MAXN = 100000+9;

int f[MAXN];

inline int read(){
	char c=getchar(); int ret=0;
	while (c<'0'||c>'9') c=getchar();
	while (c<='9'&&c>='0') ret=ret*10+c-'0', c=getchar();
	return ret;	
}

inline int quick_pow(int w, int t, int p) {
	int ret = 1;
	while (t) {
		if (t & 1) ret = (LL)ret*w % p;
		w = (LL)w*w % p; t >>= 1;
	}
	return ret;
}

inline int C(int m,int n, int p) {
	if (n > m) return 0;
	else return (LL)f[m]*quick_pow(f[m-n],p-2,p)*quick_pow(f[n],p-2,p)%p;
}

int lucas(int m, int n, int p) {
	if (n == 0) return 1;
	else return ((LL)lucas(m/p,n/p,p) * C(m%p,n%p,p)) % p;
}

int main(){
	int T = read(); while (T--) {
		int n = read(),m = read(),p = read(); 
		f[0] = 1; for (int i=1;i<=p;i++) f[i] = (LL)f[i-1]*i % p;
		printf("%d\n",lucas(n+m,n,p));
	}
	return 0;
}

另外,lucas定理可以推广到整数域上,参见:
http://www.cnblogs.com/jianglangcaijin/p/3446839.html

【BZOJ 2242】[SDOI2011] 计算器

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2242
数据生成器:http://paste.ubuntu.com/22875643/
数据传送门:http://pan.baidu.com/s/1dE71QHr

这个题,做了一个下午+一个晚上,最后才发现是把”cannot”中间多加了一个‘a’
MD,老子好想杀人! (╯‵□′)╯︵┻━┻

题目本身很简单,就是快速幂+拓展欧几里得+BSGS
但网上的程序,多少有问题,反正我能搜出来的,我都可以叉掉
我这份代码问题应该比较少吧:

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define LL long long
using namespace std;

const int MAXN = 100000;
const double EPS = 1e-5;

namespace Hash{
	const int MOD = 99971;
	int head[MAXN],nxt[MAXN],val[MAXN],data[MAXN],T;
	
	inline void init(){
		T = 0;
		memset(head,0,sizeof(head));
	}
	
	inline void insert(int w, int d){
		nxt[++T] = head[w%MOD];
		val[T] = w; data[T] = d;
		head[w%MOD] = T;
	}	
	
	inline int find(int w){
		for (int i=head[w%MOD];i;i=nxt[i])
			if (val[i] == w) return data[i];
		return -1;
	}
};

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0'){ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}

inline int quick_pow(int a,int b,int c){
	int ret = 1; while (b) {
		if (b & 1) ret = ((LL)ret * a) % c;
		a = (LL)a*a%c; b >>= 1;
	} return ret;
}

int GCD(int a, int b){return b?GCD(b,a%b):a;}

void EX_GCD(int a, int b, int &x, int &y){
	if (!b) x=1, y=0;
	else EX_GCD(b,a%b,y,x), y-=a/b*x;
}

inline int EX_GCD(int a, int b, int c) {
	int ret,tmp,gcd=GCD(a,b); 
	if (c%gcd) return -1;
	else {
		EX_GCD(a/gcd,b/gcd,ret,tmp);
		ret = (((LL)c/gcd*ret) % b + b) % b;
		return ret;
	}
}

inline void solve(int a, int b, int c) {
	int ret = EX_GCD(a,c,b); 
	if (~ret) printf("%d\n",ret);
	else printf("Orz, I cannot find x!\n");
}

inline void BSGS(int a, int b, int c) {
	Hash::init(); a %= c; b %= c;
	if (!a && b > 1) {printf("Orz, I cannot find x!\n"); return;}
	
	int lim = int(ceil(sqrt(c))+EPS);
	for (int i=0,w=1;i<=lim;i++) {
		if (w == b) {printf("%d\n",i); return;}
		Hash::insert(w,i);
		w = ((LL)w*a) % c;
	}
	
	int w_ori = quick_pow(a,lim,c), rev_ori = quick_pow(w_ori,c-2,c);
	for (int i=1,w=w_ori,rev=rev_ori;i<=lim;i++) {
		int tmp = Hash::find(((LL)rev*b) % c);
		if (tmp > -1) {printf("%d\n",i*lim+tmp); return;}
		w = ((LL)w*w_ori) % c;
		rev = ((LL)rev*rev_ori) % c;
	}
	printf("Orz, I cannot find x!\n");
}

int main(){
	int T=read(),ty=read(); while (T--) {
		int a=read(), b=read(), c=read();
		if (ty == 1) printf("%d\n",quick_pow(a,b,c));
		else if (ty == 2) solve(a,b,c);
		else BSGS(a,b,c);
	}
	return 0;
} 

【POJ 2891】Strange Way to Express Integers

题目传送门:http://poj.org/problem?id=2891

这个东西,不保证互质,所以中国剩余定理上不了
于是就得合并方程:
把\(\left\{ \begin{array}{l}
x \equiv {a_1}(Mod{\rm{ }}{b_1})\\
x \equiv {a_2}(Mod{\rm{ }}{b_2})
\end{array} \right.\)
替换成\(x \equiv {a_1} + A \cdot {b_1}(Mod{\rm{ }}\frac{{{b_1} \cdot {b_2}}}{{GCD({b_1},{b_2})}})\)
其中A为\(A \cdot {b_1} – B \cdot {b_2} = {a_2} – {a_1}\)的最小非负整数解
至于推导过程嘛,就自己推一推咯,不想写latex了,太费时间

#include<iostream>
#include<cstdio>
#define LL long long
using namespace std;

inline LL read(){
	char c=getchar(); LL ret=0,f=1;
	while (c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0'){ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}

LL GCD(LL a,LL b){return b?GCD(b,a%b):a;}

LL EX_GCD(LL a, LL b, LL &x, LL &y){
	if (!b) {x=1;y=0;}
	else {EX_GCD(b,a%b,y,x);y-=a/b*x;}
}

inline LL EX_GCD(LL a, LL b, LL pur){
	LL ret,tmp; EX_GCD(a,b,ret,tmp);
	return ((ret*pur)%b + b) % b;
}

int main(){
	int n; while (~scanf("%d",&n)) {
		LL b1=read(),a1=read(),a2,b2,tag=0,A,tmp,gcd;
		for (int i=1;i<n;i++) {
			b2 = read(); a2 = read(); gcd = GCD(b1,b2);
			if (!tag) {
				if ((a2-a1)%gcd) tag = 1, printf("-1\n");
				else { 
					A = EX_GCD(b1/gcd,b2/gcd,(a2-a1)/gcd);
					a1 = a1 + (A*b1);
					b1 = b1/gcd*b2; a1 %= b1;
				}
			}
		}
		if (!tag) printf("%lld\n",((a1%b1)+b1)%b1);
	}
	return 0;
} 

另外,这个题,貌似会爆long long,不过只要你的代码跟我的差不多,就能过QAQ
不得不说,这个题目真的是出得辣鸡,数据范围都没给全………

【POJ 1006】Biorhythms

题目传送门:http://poj.org/problem?id=1006
中文版题目:http://hzwer.com/3299.html

完全看不懂题,不过既然大家都说是模线性方程组,那就按照那样做咯!
中国剩余定理的话,看看wiki就明白啦:https://en.wikipedia.org/wiki/Chinese_remainder_theorem

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

const int SGZ = 3;
const int MAXN = 5;
const int SUM = 23*28*33;

int T,arr[]={0,28*33,23*33,23*28},ori[]={0,23,28,33},a[MAXN],d;

void EX_GCD(int a, int b, int &x, int &y){
	if (!b) x=1, y=0;
	else EX_GCD(b,a%b,y,x), y -= a/b*x;
}

inline int rev(int w, int MOD){
	int ret,tmp; EX_GCD(w,MOD,ret,tmp);
	return ret;
}

int main(){
	while (cin>>a[1]>>a[2]>>a[3]>>d && a[1] != -1) {
		int vout = 0; 
		for (int i=1;i<=SGZ;i++) vout += a[i]*rev(arr[i],ori[i])*arr[i];
		vout = ((vout-d)%SUM+SUM)%SUM; vout = vout?vout:SUM;
		printf("Case %d: the next triple peak occurs in %d days.\n",++T,vout);
	}
	return 0;
}

【BZOJ 1042】[HAOI2008] 硬币购物

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1042

这个题呢,第一眼看到就知道不会做,结果果然只会O(n*logn*t)的dp
题解的话,我们来膜byvoid吧:https://www.byvoid.com/blog/haoi-2008-coin

#include<iostream>
#include<cstdio>
#define LL long long
using namespace std;

const int SGZ = 5;
const int MAXN = 100000+9;
const int LIM = 100000;

int c[SGZ],s,d[SGZ];
LL f[MAXN],vout;

inline int read(){
	char c=getchar(); int ret = 0;
	while (c<'0'||c>'9') c=getchar();
	while (c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar();
	return ret;
}

void solve(int p, int sz, int w){
	if (p == 5) {
		if (sz & 1) vout -= f[s-w];
		else if (sz) vout += f[s-w];
	} else {
		solve(p+1,sz,w);
		if (w+d[p] <= s) solve(p+1,sz+1,w+d[p]);
	}
}

int main(){
	for (int i=1;i<=4;i++) c[i] = read(); int T = read(); f[0] = 1;
	for (int j=1;j<=4;j++) for (int i=c[j];i<=LIM;i++) f[i] += f[i-c[j]];
	while (T--) {
		for (int i=1;i<=4;i++) d[i] = read(); s = read();
		for (int i=1;i<=4;i++) d[i] = (d[i]+1)*c[i];
		vout = f[s]; solve(1,0,0);
		printf("%lld\n",vout);	
	}
	return 0;
} 

【BZOJ 1853】[Scoi2010] 幸运数字

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1853

这个题,乍一看,跟2393一样。
再一看,确实是一样的QAQ

就只有一个小问题:会不会爆long long
一开始我想,哼,怎么可能爆long long? 不是要中途除掉一个gcd吗?(¬︿¬)
于是我就在hzwer的程序上加了一个判断:如果爆了long long输出“fuck!!!”
然后效果大概是这样的:
↓↓↓↓↓↓这个是GIF,要点开才能感受到来自心灵的震撼!↓↓↓↓↓↓
1234567865432
大神我错了!!!/(ㄒoㄒ)/~~

#include<iostream>
#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;

const int MAXN = 20000;

int cnt,tot;
LL arr[MAXN],buf[MAXN],l,r,vout;

void DFS(LL w){
	if (w > r) return;
	buf[++cnt] = w;
	DFS(w*10+6);
	DFS(w*10+8);
}

inline void prework(){
	DFS(6); DFS(8);
	sort(buf+1,buf+cnt);
	for (int i=1,tag=0;i<=cnt;i++,tag=0) {
		for (int j=i-1;j;j--) if (buf[i]%buf[j]==0) {tag=1;break;}
		if (!tag) arr[++tot] = buf[i];
	} 
	for (int i=1;i*2<=tot;i++) swap(arr[i],arr[tot-i+1]);
}

LL GCD(LL a, LL b){return b?GCD(b,a%b):a;}

void solve(int step, int sz, LL w){
	if (step == tot+1) {
		if (sz & 1) vout += r/w - (l-1)/w;
		else if (sz) vout -= r/w - (l-1)/w;
	} else {
		solve(step+1,sz,w);
		double tmp = (double)w*arr[step]/GCD(w,arr[step]);
		if (tmp <= r) solve(step+1,sz+1,w/GCD(w,arr[step])*arr[step]); 
	}
}

int main(){
	scanf("%lld%lld",&l,&r);
	prework();
	solve(1,0,1);
	printf("%lld\n",vout);
	return 0;
} 

【BZOJ 2393】Cirno的完美算数教室

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2393
数据生成器:http://paste.ubuntu.com/22548407/

额,容斥第一题,竟然成了乱搞…….
这题的时间复杂度是玄学……
就是搞出互质的baka数,然后容斥

#include<iostream>
#include<cstdio>
#include<algorithm>
#define LL long long 
using namespace std;

const int MAXN = 2000;

int l,r,arr[MAXN],tmp[MAXN],vout,cnt,tot;

void DFS(LL w){
	if (w > r) return;
	tmp[++cnt] = w;
	DFS(w*10+2);
	DFS(w*10+9);
}

inline void prework(){
	DFS(2); DFS(9);
	sort(tmp+1,tmp+cnt); 
	for (int i=1,tag=0;i<=cnt;i++,tag=0) {
		for (int j=i-1;j;j--) if (tmp[i] % tmp[j] == 0) {tag = 1; break;}
		if (!tag) arr[++tot] = tmp[i];
	}
	for (int i=1;i*2<=tot;i++) swap(arr[i],arr[tot-i+1]);
}

int GCD(int a, int b){return b?GCD(b,a%b):a;}

void solve(int step, int sz, int w){
	if (step == tot+1) {
		if (sz & 1) vout += r/w - (l-1)/w;
		else if (sz) vout -= r/w - (l-1)/w;
	} else {
		solve(step+1,sz,w);
		LL buf = (LL)w/GCD(w,arr[step])*arr[step];
		if (buf <= r) solve(step+1,sz+1,buf);
	}
}

int main(){
	scanf("%d%d",&l,&r);
	prework();
	solve(1,0,1);
	printf("%d\n",vout);
	return 0;
}

【BZOJ 3450】[Tyvj 1952] Easy

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3450
数据生成器:http://paste.ubuntu.com/22437621/

额,这个题,因为之前做过一道维护平方的数据结构题目
所以没有采用全期望公式,而是用的“概率*值”的方式
之前脑抽了,写了一个用线段树的版本:http://paste.ubuntu.com/22437306/
结果写完以后,发现其实不用线段树QAQ,O(n)扫一遍就好

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

const int MAXN = 300000+9;

int n; char pat[MAXN];
long double vout,T1,T2,T3;

int main(){
	scanf("%d\n%s",&n,pat+1);
	for (int i=1;i<=n;i++) {
		if (pat[i] == 'o') {
			if (pat[i-1] == 'o') continue;
			else { 
				long double p = pat[i-1]=='?'?0.5:1;
				T1 += p*i*i; T2 += p*i; T3 += p;
			}
		} else if (pat[i] == 'x') {
			vout += T1 - T2*2*i + T3*i*i;
			T1 = T2 = T3 = 0; 
		} else {
			vout += (T1 - T2*2*i + T3*i*i)*0.5;
			T1 /= 2; T2 /= 2; T3 /= 2; 
			if (pat[i-1] == 'o') continue;
			long double p = pat[i-1]=='?'?0.25:0.5;
			T1 += p*i*i; T2 += p*i; T3 += p;		
		}
	} vout += T1 - T2*2*(n+1) + T3*(n+1)*(n+1);
	printf("%.4lf\n",(double)vout);
	return 0;
} 

hzwer就是用全期望公式搞的,那样的话看起来比较自然一点:
http://hzwer.com/2838.html

【BZOJ 1415】[Noi2005] 聪聪和可可

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1415
数据生成器:http://paste.ubuntu.com/22417252/

论文题,记忆化深搜
我在处理该去哪时,用的三方的floyd
然而hzwer告诉我,平方的bfs就好QAQ,反正没有T,我就不改啦!(づ ̄ 3 ̄)づ

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

const int MAXN = 1000+9;
const int INF = 1000000;
const double sta = -0.5;

int n,m,C,M,d[MAXN][MAXN],to[MAXN][MAXN],cnt[MAXN];
double ans[MAXN][MAXN];

inline int read(){
	char c=getchar(); int ret=0;
	while (c<'0'||c>'9') c=getchar();
	while (c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar();
	return ret;
}

inline void Floyd(){
	for (int k=1;k<=n;k++) for (int i=1;i<=n;i++) if (d[i][k] < INF) 
		for (int j=1;j<=n;j++) d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
	
	for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) for (int k=1;k<=n;k++) 
		if (d[i][k] == 1 && d[k][j] == d[i][j]-1) {to[i][j] = k; break;}
	for (int i=1;i<=n;i++) to[i][i] = i;
}

double Get_Ans(int u, int v){
	if (ans[u][v] > sta) return ans[u][v];
	else {
		if (to[to[u][v]][v] != v) {
			ans[u][v] = Get_Ans(to[to[u][v]][v],v)+1;
			for (int i=1;i<=n;i++) if (d[v][i] == 1) ans[u][v] += Get_Ans(to[to[u][v]][v],i)+1;
			return ans[u][v] /= cnt[v];
		} else return ans[u][v] = 1;
	}
}

int main(){ 
	n = read(); m = read(); C = read(); M = read();
	for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) d[i][j] = INF, ans[i][j] = -1;
	for (int i=1;i<=n;i++) d[i][i] = 0, ans[i][i] = 0, cnt[i] = 1;
	for (int i=1,a,b;i<=m;i++) cnt[a = read()]++, cnt[b = read()]++, d[a][b] = d[b][a] = 1;
	Floyd(); printf("%.3lf\n",Get_Ans(C,M));
	return 0;
} 

【COGS 1478】麻球繁衍

题目传送门:http://cogs.top/cogs/problem/problem.php?pid=1487

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;

const int MAXN = 1000+9;

int n,m;
double w,p[MAXN],vout;

inline int read(){
	char c=getchar(); int buf=0,f=1;
	while (c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0'){buf=buf*10+c-'0';c=getchar();}
	return buf*f;
}

int main(){
	freopen("tribbles.in","r",stdin);
	freopen("tribbles.out","w",stdout);
	for (int T=1,TT=read();T<=TT;T++){
		n = read(), w = read(); m = read(); 
		for (int i=0;i<n;i++) scanf("%lf",&p[i]);
		vout = p[0];
		for (int k=2;k<=m;k++) {
			double tmp = 0;
			for (int i=0;i<n;i++) tmp += p[i]*pow(vout,i);
			vout = tmp; 
		}
		printf("Case #%d: %.7lf\n",T,pow(vout, w));
	} 
	return 0;
} 

【Codeforces 701D】As Fast As Possible

题目传送门:http://codeforces.com/contest/701/problem/D

额,懵逼了一晚上,结果今天看里约奥运会的时候不小心睡着了(⊙﹏⊙)
在睡梦中,突然发现:貌似根据汽车和人各列一个方程即可QAQ

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std; 

double x,n,k,v1,v2,l;

int main(){
	cin>>n>>l>>v1>>v2>>k;
	x = l*(v1+v2)/(v1*(ceil(n/k)*2-1)+v2);
	printf("%.10lf\n",x/v2+(l-x)/v1);
	return 0;
}

【SOJ 1718】特工

题目传送门:http://oi.cyo.ng/wp-content/uploads/2016/08/216378216437812.png
题解传送门:http://oi.cyo.ng/wp-content/uploads/2016/08/123478756.png
OJ传送门:http://oi.cdshishi.net:8080/Problem/1718

考试的时候,看一眼数据范围
马上反应过来开是FFT,而且坚信是FFT
结果后来没想出来,考完试还被lcr给D了………
结果最后一看,真™是FFT!

这个题,看一眼马上想到高斯消元
于是考试的时候就写了O(n^3)的高斯消元

后来讲题的时候,说到其实就是一个矩阵求逆,我很赞同
于是就来补O(n^3)的矩阵求逆啦!

#include<iostream>
#include<cstdio>
#define LL long long
#define abs(x) ((x)<0?-(x):(x))
using namespace std;

const int MAXN = 3000;
const double EPS = 1e-8;

int n;
double mat[MAXN*2][MAXN],vout[MAXN],arr[MAXN];

inline LL read(){
	char c=getchar(); LL ret=0,f=1;
	while (c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0'){ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}

#define lowbit(x) ((x)&-(x))
inline int bitcount(int w){
	int ret = 0;
	while (w) ret++, w -= lowbit(w);
	return ret;
}

int LCA(int a, int b){
	if (!b) return a;
	else return LCA(b,a%b);
}

inline void Get_Reverse_Matrix(){
	for (int i=1,w;i<=n;i++) {
		for (w=i;w<=n;w++) if (mat[i+n][w]) break;
		for (int j=1;j<=n*2;j++) swap(mat[j][w], mat[j][i]);
		if (abs(mat[i+n][i]) < EPS) continue;
		else for (int j=1;j<=n;j++) if (abs(mat[i+n][j]) > EPS && j != i) {
			double tmp = mat[i+n][j]/mat[i+n][i];
			for (int k=1;k<=n*2;k++) mat[k][j] -= mat[k][i]*tmp;
		}
		double tmp = mat[i+n][i];
		for (int j=1;j<=n*2;j++) mat[j][i] /= tmp;
	}
}

int main(){
	n = read(); 
	for (int i=1;i<=n;i++) arr[i] = read();
	for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) 
		mat[i+n][j] = (bitcount((i-1|j-1)^i-1)+1) % 2;
	for (int i=1;i<=n;i++) mat[i][i] = 1;
	Get_Reverse_Matrix();
	for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) 
		vout[i] += mat[i][j]*arr[j];
	for (int i=1;i<=n;i++) printf("%d ",(int)(vout[i]+EPS));
	return 0;
}

std是应用数据特点,把矩阵求逆给搞到了O(n^logn)
我就不写程序啦,反正SOJ代码可以看

—————————— UPD 2017.7.3 ——————————
仔细想一想,似乎按二进制分治也可以做?

【COGS 1489】玩纸牌

题目传送门:http://cogs.top/cogs/problem/problem.php?pid=1489

这一道题真的是坑啊!
总体思路肯定是:先DP出每天完成计划的概率,然后再根据期望的定义来算

然后就是坑点了。
不难得出,转移方程:\(p(i,j) = p(i – 1,j – 1) \cdot {p_{win}} + p(i,j – 1) \cdot {p_{lose}}\)
然而这是错的!因为如果i/j满足条件的话,这个货就会立即停止,即i/j>a/b的概率为0
妈妈啊!wa了好久!

不过这题还是学了一点东西:
1. 枚举的时候,遇到分数,为了避免误差可以变除为乘
2. 全期望公式,不仅可以用来装逼,还可以用来化简式子,避免误差
比如这个题目,使用全期望公式,可以化到:\(ans = \frac{1}{{lose}}\)

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

const int MAXN = 1000+9;
const double EPS = 1e-15;

double t1[MAXN],t2[MAXN],win,lose,*p1,*p2;
int n,a,b;

inline double cal(double ty){
	memset(t1,0,sizeof(t1));
	memset(t2,0,sizeof(t2));
	p1 = t1; p2 = t2; p2[0] = 1;
	
	for (int j=1;j<=n;j++) {
		for (int i=0;i*b<=a*j;i++) p1[i] = p2[i]*lose + ((i)?p2[i-1]*win:0);
		swap(p1, p2);
	}
	
	double ret = 0;
	for(int i=0;i*b<=a*n;i++) ret += p2[i];
	return ret;
}

int main(){
    freopen("expected.in","r",stdin);
    freopen("expected.out","w",stdout);
	int TT; cin>>TT;
	for (int T=1;T<=TT;T++){
		scanf("%d/%d %d",&a,&b,&n);
		win = (double)a/b; lose = 1-win;
		lose = cal(win); win = 1.0 - lose;
		double  w = 1, vout = 0; int day = 1;
		while (w > EPS) {
			vout += w*lose*day;
			w *= win; day++;
		}
		printf("Case #%d: %d\n",T,(int)(vout+0.05));
	}
	return 0;
} 

【BZOJ 1076】[SCOI2008] 奖励关

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1076
数据生成器:http://paste.ubuntu.com/20577780/

概率DP第一题! 撒花~ *★,°*:.☆\( ̄▽ ̄)/$:*.°★*
虽然想了很久很久,做了一个下午,但彻底懂啦!

设f[i][k]为状态为i,走了k步时到终点的期望得分
根据全期望公式,得到以下转移方程:
\(f[i][k] = \sum\limits_{j = 1}^n {{p_j} \cdot \max ((f[i|{2^{j – 1}}][k + 1] + val[j]) \cdot ((i\& \lim [j]) = = \lim [j]),f[i][k + 1])}\)
其中直接取max的正确性在于:只要是合法的状态,那么最后都会被计入答案
然后根据DAG的性质,倒着递推即可。

但今天想了一下午,不仅相同了这个,还有更重要的收获:
为什么不能正着推?
不是网上说的会推出无效的状态,而是:
题目上只给了\({P_{(u \to v)}}\),而没有给\({P_{(v \to u)}}\)
而且\({P_{(v \to u)}}\)你还算不出来,因为分母不确定(受转移的影响)。
所以你只能按照上面的方式列方程,进而只能倒推

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

const int MAXN = 80000;
const int KK = 100+9;

int val[MAXN],n,K,ty[MAXN],MX,lim[MAXN];
double f[KK][MAXN];

int main(){
	scanf("%d%d",&K,&n);
	for (int i=1;i<=n;i++) ty[i] = 1<<(i-1);
	for (int i=1,tmp;i<=n;i++) {
		scanf("%d",&val[i]);
		while (scanf("%d",&tmp) && tmp)
			lim[i] |= ty[tmp];
	}
	MX = ty[n]*2-1;
	
	for (int k=K;k;k--) for (int i=0;i<=MX;i++) for (int j=1;j<=n;j++) 
		f[k][i] += max((f[k+1][i|ty[j]]+val[j])*((i&lim[j]) == lim[j]),f[k+1][i])/n;
	
	printf("%.6lf",f[1][0]);
	return 0;
}