原题: http://hihocoder.com/contest/challenge5
解答
=========
A 字体
这个题是线段树
B 魔法
这个题思路很简单,n个箱子构成m个轮换,每个轮换为 t1->t2->t3..->tj, (即t1箱子含有t2的钥匙,t2的箱子含有t3的钥匙...)
现在要求的概率是,从n个箱子中挑出k个,他们分布在所有的m个轮换中的概率。
- 求出挑k个箱子恰好分布在m个轮换中,这是一个分组背包问题:
- 每个轮换是一个背包分组,容量为s, 里面每个物品i 对应从该轮换中挑出i个箱子(显然0<i<=k)。
- dp地装入某个背包分组: cnt1[ 0 <= x <= k ] = E { cnt [ 0 <= x-i <=k ] * C( s, i ) , 其中 0<i<=k }
一开始想得比较复杂,C(300, x )是会爆long long int的, 后来发现数据很弱。
C 与链
这个题是对二进制位的分组背包,
- 假设该位为1的ai 个数为j, 那么 个数j的取值可以为0到k, 那么j的每种取值就是取一个物品;
- 又因为个数j 只能取一个值,就相当于这些物品(或取值)构成一个背包分组(或一个新的虚拟物品)
- 然后多个2进制位构成多个背包分组。
代码
=========
B 魔法解箱子算概率
分组背包 O(K^2 * m ), m为轮换个数即物品分组个数, K为背包容量, 同时也是 每个分组的物品个数的上限
#include<iostream>
#include<iomanip>
using namespace std;
#include <string.h>
#include <vector>
typedef long double ll;
ll a[301][301];
void ca(){
memset(a, 0, sizeof(a));
for(int i=1; i<301; i++) a[0][i]=0;
for(int i=0; i<301; i++) a[i][0]=1;
for(int i=1; i<301; i++){
for(int j=1; j<301; j++){
a[i][j] = a[i-1][j] + a[i-1][j-1];
}
}
}
double calc(vector<int> &ss, int n, int K){
int l = ss.size();
// l ge beibao
vector<ll> cnt(K+1, 0), tmp;
cnt[0]=1;
for(int i=0; i<l; i++){
tmp=vector<ll>(K+1, 0);
for(int j=1; j <= ss[i] && j <= K; j++){
for(int k=0; k<=K-j; k++){
// tmp[k+j] = tmp[k+j] + cnt[k];//@error
tmp[k+j] = tmp[k+j] + cnt[k]*a[ss[i]][j];
}
}
cnt=tmp;
}
//cout<<cnt[K]<<":"<<a[n][K]<<endl;
return cnt[K]/a[n][K];
}
int main(){
int casen; cin>>casen;
ca();
for(int casei=0; casei<casen; casei++){
int n, k;
cin>>n>>k;
vector<int> s(n, -1);
for(int i=0; i<n; i++){
//cin>>s[i]; @error
int a; cin>>a; s[i]=a-1;
}
vector<bool> visit(n, false);
vector<int> ss;
for(int i=0; i<n; i++){
if(!visit[i]){
int cnt=0;
int j = i;
while(!visit[j])
cnt++, visit[j]=true, j=s[j];
ss.push_back(cnt);
//cout<<":"<<cnt<<endl;
}
}
cout<<fixed<<setprecision(9)<<calc(ss, n, k)<<endl;
}
return 0;
}
C 与链 (分组背包, O(n*k) )
#include <iostream>
#include <vector>
using namespace std;
typedef long long int ll;
#define MOD 1000000009
int calc(int n, int k){
vector<ll> cnt(n+1, 0);
vector<ll> tmp;
cnt[0]=1;
for(int step=1; step<=n; step*=2){
tmp = cnt;
for(int i=1; i<=k; i++){
for(int j=0; j<=n-step*i; j++){
tmp[j+step*i]+=cnt[j];
tmp[j+step*i]%=MOD;
}
}
cnt=tmp;
}
return cnt[n];
}
int main(){
int N; cin>>N;
for(int casei=0; casei<N; casei++){
int n,k ;
cin>>k>>n;
cout<<calc(n, k)<<endl;
}
return 0;
}