Bzoj 3652: 大新闻(数位dp)

本文探讨了数位DP在解决特定类型问题时的应用,通过分析大新闻问题,详细介绍了如何设计状态转移方程,利用枚举状态法解决复杂问题,并提供了一段C++代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以下内容来自ShallWe's Blog

题目

3652: 大新闻

Description

1.jpg

Input

2.jpg

Output

3.jpg
4.jpg
\(1<=N<=10^18\)

解题报告

显然是一道数位dp的题目,观察数据范围可知.
加密和不加密是完全没有关系的两个问题。

  1. 加密:等概率的选取两个数异或的期望?\(ans_1*n^2=\Sigma_{i=1}^{W}f_i*(n-f_i)*2*(1<<i-1)\)非常显然,就是观察每个数位可以做出的贡献。
  2. 未加密:对于\(x∈[0,n)\),寻找\(y\)使得\(x^y \to max\),将二进制推广成树状,那么显然最有的策略是对称的走,这样可以让每个数位都做出贡献,而如果不可以对称,就退而求其次好了,,,

对数位dp问题,我一向是很的,学长安利了一种具有普遍性的方法->疯狂枚举状态法,就是设计\(f[i][0/1][0/1][][][]\)这个姿势的式子,\(i\)表示枚举到的数位,后面的每个括号都是消除后效性的状态,然后枚举出所有的状态,显然有稳定的复杂度,和很好写的脸
回到大新闻,f[i][1/0][1/0]表示从高开始到第\(i\)位,\(x\)是否卡边界,\(y\)是否卡边界的概率(存收益,期望都没问题)。然后就是很短(?)的代码了。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
using namespace std;
const int N=70;
typedef long long ll; 
ll n,mi[N],have[N];
double f[N][2][2],g[N][2][2],p;  
int a[N],w; 
double one(){
    int i,j,k,aa,bb,jj,kk; 
    double ans=0; 
    g[a[0]+1][1][1]=1.0/(double)n; 
    for (i=a[0];i;i--) 
        for (j=0;j<=1;j++)
            for (k=0;k<=1;k++)
                if (g[i+1][j][k]>0.0)
                    for (aa=0;aa<=1;aa++){
                        if (j)
                            if (aa<a[i]) 
                                jj=0; 
                            else
                                if (aa==a[i]) 
                                    jj=1; 
                                else
                                    continue;
                        else
                            jj=0; 
                        if (k){
                            bb=aa^1; 
                            if (bb>a[i]) bb^=1; 
                            if (bb<a[i]) 
                                kk=0; 
                            else
                                kk=1; 
                        }else
                            bb=aa^1,kk=0; 
                        f[i][jj][kk]+=f[i+1][j][k]+(double)mi[i-1]*g[i+1][j][k]*(double)(aa^bb); 
                        g[i][jj][kk]+=g[i+1][j][k];
                    }
    for (i=0;i<=1;i++)
        for (j=0;j<=1;j++) 
            ans+=f[1][i][j]; 
    return ans; 
}   
double two(){
    ll x=n;a[0]=0;
    double ans=0;
    while(n){
        a[++a[0]]=(int)(n%2);
        n/=2;
    }
    int i,j;
    for (n=x,i=a[0];i;i--){
        if (a[i]){
            n-=mi[i-1],have[i]+=n;
            for (j=i-1;j;j--) 
                have[j]+=(i-2>=0?mi[i-2]:0LL);
        }
    }
    for (n=x,i=a[0];i;--i)
        ans+=(double)have[i]/(double)n*(double)(n-have[i])/(double)n*2.0*(double)mi[i-1];
    return ans;
}                                           
int main(){
//  freopen("bignews.in","r",stdin); 
//  freopen("bignews.out","w",stdout);
    scanf("%lld",&n);
    scanf("%lf",&p);
    ll x=n-1;
    while (x){
        a[++a[0]]=(int)(x%2); 
        x/=2;
    }
    mi[0]=1LL;
    for (int i=1;i<=a[0];i++) 
        mi[i]=mi[i-1]*2LL; 
    printf("%lf\n",one()*p+two()*(1.0-p));
    return 0; 
}

转载于:https://www.cnblogs.com/ShallWe2000/p/5774014.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值