Blocks
期望dp,从已经满足的点倒着推,首先考虑状态,发现 n n n很小,直接状压,然后暴力枚举状态看是否全部覆盖,发现坐标跨度很大,对坐标离散化,依次差分修改, O ( n 2 2 n ) O(n^22^n) O(n22n),然后就可以直接dp了
d p i = ∑ j d p i [ ( 1 < < j ) & i ] + 1 + ∑ j d p i ∣ j + 1 [ ( 1 < < j ) & 1 = = 0 ] n dp_i={{\sum_j dp_i [(1<
化简得: d p i = n + ∑ j d p i ∣ j [ ( 1 < < j ) & 1 = = 0 ] n − c n t 1 ( i ) dp_i={ n+\sum_j dp_{i|j} [(1<
#include
#define ll long long
const ll mod=998244353;
int x1[15],x2[15],y2[15];
int sum[50][50];
ll qpow(ll x,ll p){
ll ans=1;
x%=mod;
while (p){
if (p&1) ans=ans*x%mod;
x=x*x%mod;
p>>=1;
}
return ans;
}
ll ni(ll x){
return qpow(x,mod-2);
}
void solve(){
int n,w,h;
std::cin>>n>>w>>h;
std::vector<int> y1(n);
std::vector<int> temp;
for (int i=0;i<n;i++){
std::cin>>x1[i]>>y1[i]>>x2[i]>>y2[i];
temp.push_back(x1[i]);
temp.push_back(x2[i]);
temp.push_back(y1[i]);
temp.push_back(y2[i]);
}
temp.push_back(0);
temp.push_back(w);
temp.push_back(h);
std::sort(temp.begin(),temp.end());
int cnt=0;
std::map<int,int> mp;
for (int i=0;i<temp.size();i++){
if (i==0||temp[i]!=temp[i-1]){
mp[temp[i]]=++cnt;
}
}
w=2*mp[w]-1;
h=2*mp[h]-1;
for (int i=0;i<n;i++){
x1[i]=std::min(w,2*mp[x1[i]]-1);
x2[i]=std::min(w,2*mp[x2[i]]-1);
y1[i]=std::min(h,2*mp[y1[i]]-1);
y2[i]=std::min(h,2*mp[y2[i]]-1);
}
auto f=[&](int x){
memset(sum,0,sizeof(sum));
for (int i=0;i<n;i++){
if (!((1<<i)&x)) continue;
sum[x1[i]][y1[i]]++;
sum[x1[i]][y2[i]+1]--;
sum[x2[i]+1][y1[i]]--;
sum[x2[i]+1][y2[i]+1]++;
}
for (int i=1;i<=w;i++){
for (int j=1;j<=h;j++){
sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
if (!sum[i][j]) return -1;
}
}
return 0;
};
std::vector<ll> dp(1<<n);
for (int i=(1<<n)-1;i>=0;i--){
dp[i]=f(i);
if (i==(1<<n)-1&&dp[i]==-1){
std::cout<<"-1\n";
return;
}
}
for (int i=(1<<n)-1;i>=0;i--){
if (dp[i]==0) continue;
dp[i]=0;
int cnt=0;
for (int j=0;j<n;j++){
if ((1<<j)&i){
cnt++;
}else{
dp[i]=(dp[i]+dp[i|(1<<j)])%mod;
}
}
dp[i]=(dp[i]+n)%mod;
dp[i]=dp[i]*ni(n-cnt)%mod;
}
std::cout<<dp[0]<<"\n";
}
P3239 [HNOI2015] 亚瑟王
一张卡牌一整局只会产生一次贡献,不妨直接考求出这一次贡献所产生的的概率为多少。
设 g i g_i gi表示第 i i i张牌在 r r r轮中发动一次技能的概率,发现这个东西不太好找前后的递推关系。我们发现当前卡牌是否发动技能的概率受到前面卡牌的影响(前面选了几张卡,那么我再怎么也要有几轮把前面的卡选完),但不受到后面卡牌的影响,所以我们设 f i , j f_{i,j} fi,j,表示在这 r r r轮中,前 i i i张牌有 j j j张牌被选的概率,这样我们就知道有多少轮能在 i i i这个地方做出决策(产生被选的概率),从而算出 g i g_i gi
然后 f i , j f_{i,j} fi,j每次只要看在 r r r轮中 i i i这张卡牌是否产生发动一次技能,分类转移
double ksm(double x,int p){
double ans=1;
while (p){
if (p&1) ans=ans*x;
x*=x;
p>>=1;
}
return ans;
}
void solve(){
int n,r;
std::cin>>n>>r;
std::vector<double> p(n+1),d(n+1);
for (int i=1;i<=n;i++){
std::cin>>p[i]>>d[i];
}
std::vector<std::vector<double>> f(n+1,std::vector<double>(r+1));
f[0][0]=1;
for (int i=1;i<=n;i++){
for (int j=0;j<=r;j++){
f[i][j]=f[i-1][j]*ksm(1-p[i],r-j);
if (j) f[i][j]+=f[i-1][j-1]*(1-ksm(1-p[i],r-j+1));
}
}
std::vector<double> g(n+1);
double ans=0;
for (int i=1;i<=n;i++){
for (int j=0;j<=r;j++){
g[i]+=f[i-1][j]*(1-ksm(1-p[i],r-j));
}
ans+=g[i]*d[i];
}
std::cout<<std::fixed<<std::setprecision(10)<<ans<<"\n";
}
P3750 [六省联考 2017] 分手是祝愿
50%的数据 k = = n k==n k==n,也就是说只要找到最小的关灯次数
直接从后往前遍历,当前点为 i i i,如果现在 i i i这个位置为1,那只能在这按一次开关,然后 O ( n ) O(\sqrt n) O(n)去修改其它位置的状态。
考虑有等概率随机按开关的情况,其实在前面我们就能知道,给出的数据我们一共要按的开关就只有那几个,一个开关重复按两次是没有效果的,按错了开关到最后也得按回去,所以我们状态设计就是已经按了几个要按的开关, f i f_i fi表示已经按了 i i i个要按的开关的期望。
i < = k , f i = k i<=k,f_i=k i<=k,fi=k
i > k i>k i>k 我们分类按对了开关或按错了开关:
f i = i n ⋅ f i − 1 + n − i n ⋅ f i + 1 f_i={i \over n}\cdot f_{i-1}+{n-i \over n} \cdot f_{i+1} fi=ni⋅fi−1+nn−i⋅fi+1
令 f i = f i − 1 + b i f_i=f_{i-1}+b_i fi=fi−1+bi b i b_i bi代入式子中解出 b i b_i bi的递推式,然后根据推出 f i f_i fi
另一种状态设计就是 f i f_i fi表示剩下 i i i个开关要按,到剩下 i − 1 i-1 i−1个开关要按的期望为多少
f i = i n + n − i n ( 1 + f i + 1 + f i ) f_i={i \over n}+{n-i \over n}(1+f_{i+1}+f_{i}) fi=ni+nn−i(1+fi+1+fi) 然后移项即可得到递推式
最后累加
代码是第一种方法
#include
#define ll long long
const ll mod=100003;
ll ksm(ll x,ll p){
ll ans=1;
x%=mod;
while (p){
if (p&1) ans=ans*x%mod;
x=x*x%mod;
p>>=1;
}
return ans;
}
ll ni(ll x){
return ksm(x,mod-2);
}
void solve(){
int n,k;
std::cin>>n>>k;
std::vector<int> a(n+1);
for (int i=1;i<=n;i++){
std::cin>>a[i];
}
std::vector<ll> b(n+1);
b[n]=1;
int cnt=0;
for (int i=n;i>=1;i--){
if (a[i]){
cnt++;
for (int j=1;j*j<=i;j++){
if (i%j==0){
a[j]^=1;
if (i/j!=j) a[i/j]^=1;
}
}
}
if (i<n){
b[i]=(n-i+mod)%mod*b[i+1]%mod*ni(i)%mod+n*ni(i)%mod;
b[i]%=mod;
}
}
std::vector<ll> f(n+1);
for (int i=1;i<=k;i++){
f[i]=i%mod;
}
for (int i=k+1;i<=cnt;i++){
f[i]=(f[i-1]+b[i])%mod;
}
ll ans=f[cnt];
for (int i=1;i<=n;i++){
ans=ans*i%mod;
}
std::cout<<ans;
}