[bzoj4513][SDOI2016]储能表

题目大意

n1i=0m1j=0max(i xor jp,0)

数位DP

首先先把n和m都减一。
接着分解成二进制。
设f[i][s1][s2][s3][s4]表示做到第i位,此时有多少二元组(a,b)满足以下条件:
s1=0,目前a已经小于n。s1=1,目前a等于n。
s2=0,目前b已经小于m。s2=1,目前b等于m。
s3=0,目前a^b已经大于p。s3=1,目前a^b等于p。
然后s4=0表示符合条件的个数,s4=1表示符合条件的a^b的和。
然后答案显然。
转移也显然,我的代码已经很优美了!

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const ll maxd=61;
ll two[maxd+10];
ll f[maxd+10][2][2][2][2];
ll a[maxd+10],b[maxd+10],c[maxd+10];
ll i,j,k,l,r,t,n,m,p,mo,ca,ans;
void add(ll &x,ll y){
    x=(x+y)%mo;
}
int main(){
    freopen("table.in","r",stdin);freopen("table.out","w",stdout);
    two[0]=1;
    fo(i,1,maxd) two[i]=two[i-1]*2;
    scanf("%lld",&ca);
    while (ca--){
        scanf("%lld%lld%lld%lld",&n,&m,&p,&mo);
        n--;m--;
        fo(i,0,maxd) a[i]=(n/two[i])%2;
        fo(i,0,maxd) b[i]=(m/two[i])%2;
        fo(i,0,maxd) c[i]=(p/two[i])%2;
        fo(i,0,maxd+1)
            fo(j,0,1)
                fo(k,0,1)
                    fo(l,0,1)
                        fo(r,0,1)
                            f[i][j][k][l][r]=0;
        f[maxd+1][1][1][1][0]=1;
        f[maxd+1][1][1][1][1]=0;
        fd(i,maxd+1,1){
            fo(j,0,1)
                fo(k,0,1)
                    fo(l,0,1){
                        if (!f[i][j][k][l][0]&&!f[i][j][k][l][1]) continue;
                        fo(r,0,(j?a[i-1]:1))
                            fo(t,0,(k?b[i-1]:1)){
                                if ((r^t)<(l?c[i-1]:0)) continue;
                                add(f[i-1][j?(a[i-1]==r):0][k?(b[i-1]==t):0][l?(c[i-1]==(r^t)):0][0],f[i][j][k][l][0]);
                                add(f[i-1][j?(a[i-1]==r):0][k?(b[i-1]==t):0][l?(c[i-1]==(r^t)):0][1],f[i][j][k][l][1]+two[i-1]%mo*(r^t)%mo*f[i][j][k][l][0]%mo);
                            }
                    }
        }
        ans=0;
        fo(i,0,1)
            fo(j,0,1)
                ans=((ans+f[0][i][j][0][1]%mo-f[0][i][j][0][0]%mo*(p%mo)%mo)%mo+mo)%mo;
        printf("%lld\n",ans);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值