ZJOI模拟 数字【数位dp】

题目描述:

已知 T , L x , R x , L y , R y ,问在满足 L x x R x , L y y R y , x   o r   y = T 的情况下, W = x   a n d   y 有多少种取值。

解题思路:

数位 DP 会变得比较方便。
考虑一个数 W,我们要判断是否存在一组 (x,y) 使得 Lx ≤ x ≤ R x ,Ly ≤ y ≤ R y 且
x ∨ y = T,x ∧ y = W。
这个过程就是从高位到低位构造 (x,y) 的过程。
• 若 T 的当前位为 0,W 的当前位为 0,则 x 和 y 当前位都填 0。
• 若 T 的当前位为 0,W 的当前位为 1,则这是一个不合法的状况。
• 若 T 的当前位为 1,W 的当前位为 0,则当前位上可能 x 填 0,y 填 1,
也可能 x 填 1,y 填 0。
• 若 T 的当前位为 1,W 的当前位为 1,则 x 和 y 当前位都填 1。
x 和 y 各自已经填了的部分可能挨着Rx与 Ry或Lx与Ly的上界 (即 x 或 y 的已填部
分是 R x 或 R y或Lx或Ly 的前缀),也可能不挨,这样就有 3 × 3 = 9 种状态。
之前提到的第三种情况,同一步有两种填法,会造成分叉,所以,如果我们
要判断一个 W 是否可行,在从高位到低位确定 x,y 的过程中,就需要把这9
种状态分别可不可能出现记下来,这是一个 9 位的二进制数。
如何用 DP 的方式算出可行的 W 的种数呢?DP 的状态是当前已经考虑过
的位数和那个 9 位的二进制数。时间复杂度 O(logT),常数为 512。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=65;
ll T,l1,l2,r1,r2,f[N][1<<9];
int cnt,g[3][3];
pair<int,int> sta[9];
ll dfs(int i,int s)
{
    if(i==-1)return 1;
    if(f[i][s]!=-1)return f[i][s];
    ll res=0;
    for(int p=0;p<=(T>>i&1);p++)
    {
        int cur=0;
        for(int x=0;x<=1;x++)
            for(int y=0;y<=1;y++)if((x|y)==(T>>i&1)&&(x&y)==p)
                for(int k=0;k<9;k++)if(s>>k&1)
                {
                    int o1=sta[k].first,o2=sta[k].second;
                    ll k1,k2;
                    if(o1==0)k1=r1>>i+1;
                    else if(o1==1)k1=l1>>i+1;
                    else k1=(l1>>i+1)+1;
                    if(o2==0)k2=r2>>i+1;
                    else if(o2==1)k2=l2>>i+1;
                    else k2=(l2>>i+1)+1;
                    k1=(k1<<1)+x;k2=(k2<<1)+y;
                    if(k1>(r1>>i)||k1<(l1>>i))continue;
                    if(k2>(r2>>i)||k2<(l2>>i))continue;
                    if(k1==(r1>>i))o1=0;
                    else if(k1==(l1>>i))o1=1;
                    else o1=2;
                    if(k2==(r2>>i))o2=0;
                    else if(k2==(l2>>i))o2=1;
                    else o2=2;
                    cur|=1<<g[o1][o2];
                }
        if(cur)res+=dfs(i-1,cur);
    }
    return f[i][s]=res;
}
int main()
{
    freopen("number.in","r",stdin);
    freopen("number.out","w",stdout);
    scanf("%lld%lld%lld%lld%lld",&T,&l1,&r1,&l2,&r2);
    for(int i=0;i<3;i++)
        for(int j=0;j<3;j++)g[i][j]=cnt,sta[cnt++]=make_pair(i,j);
    memset(f,-1,sizeof(f));
    cout<<dfs(60,1)<<'\n';
    return 0;
}

猜你喜欢

转载自blog.csdn.net/cdsszjj/article/details/79966984