4513: [Sdoi2016]储能表

11 篇文章 0 订阅

省选被这题虐。。。前段时间推了一晚上终于推出dp式子;
正解是去发现神奇性质,然而数位dp也可以过;

#include<bits/stdc++.h>
#define rep(i,k,n) for(int i=k;i<=n;i++)
#define rep2(i,k,n) for(int i=k;i>=n;i--)
using namespace std;
const int N=105;
typedef long long ll;
ll T,n,m,k,p,f[N][2][2][2],g[N][2][2][2],bin[N];//low to high f[len][n][m][k]
int a[N],b[N],c[N];
void upd(ll& x,ll y){
    x+=y;while(x>=p)x-=p;
}
void init(){
    bin[1]=1;rep(i,2,63)bin[i]=(bin[i-1]<<1)%p;
    rep(i,1,63)a[i]=(n>>(i-1))&1;
    rep(i,1,63)b[i]=(m>>(i-1))&1;
    rep(i,1,63)c[i]=(k>>(i-1))&1;
    memset(f,0,sizeof(f));
    memset(g,0,sizeof(g));
}
void solve(){
    f[0][0][0][1]=f[0][1][0][1]=f[0][0][1][1]=f[0][1][1][1]=1;
    rep(l,0,62){
        rep(d1,0,1)rep(d2,0,1)rep(kk,0,1)if(f[l][d1][d2][kk]){

            ll res=f[l][d1][d2][kk];
            ll res2=g[l][d1][d2][kk];
            rep(c1,0,1)rep(c2,0,1){
                int op_k=((c1^c2)>c[l+1] ? 1 : ((c1^c2)==c[l+1] && kk));
                int op_n=(((d1 && c1==a[l+1])||(!d1 && c1<a[l+1]))<<1)|(d1^1);
                int op_m=(((d2 && c2==b[l+1])||(!d2 && c2<b[l+1]))<<1)|(d2^1);

                rep(to_n,0,1)rep(to_m,0,1)
                    if(((op_n>>to_n)&1) && ((op_m>>to_m)&1)){
                        upd(f[l+1][to_n][to_m][op_k],res);
                        upd(g[l+1][to_n][to_m][op_k],(res*(c1^c2)*bin[l+1]%p+res2)%p);
                    }
            }
        }
    }
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%lld%lld%lld%lld",&n,&m,&k,&p);n--,m--;
        init();
        solve();
        ll ans=g[63][1][1][1]-(k%p)*f[63][1][1][1]%p;
        while(ans<0)ans+=p;
        printf("%lld\n",ans);
    }
//  rep(i,1,63)printf("%lld\n",g[i][1][1][1]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值