bzoj4513: [Sdoi2016]储能表

题目

题解:

f[i][0/1][0/1][0/1] 表示从高到低第i位,是否到n的上界,是否到m的上界,当前异或结果是否到k的下界
对于每个状态记录合法方案数和所有合法i,j的异或和,最后把k的那些减去即可
摘自洛谷Fizzmy的题解

标程:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pii;
#define F first
#define S second
int T,p,vis[70][2][2][2],mx;
pii ans,f[70][2][2][2];
ll n,m,k;
void count(ll n){
    ll tmp=n;
    int cnt=0;
    while (tmp) tmp>>=1,cnt++;
    mx=max(mx,cnt);
}
void add(ll &x,ll y){
    x+=y;
    if (x>=p) x-=p;
}
pii dfs(int len,bool n1,bool m1,bool k1){
    if (len>mx) return make_pair(1,0);
    if (vis[len][n1][m1][k1]) return f[len][n1][m1][k1];
    vis[len][n1][m1][k1]=1;
    int nn=(n>>mx-len)&1,mm=(m>>mx-len)&1,kk=(k>>mx-len)&1;
    for (int i=0;i<=(n1?nn:1);i++)
        for (int j=0;j<=(m1?mm:1);j++){
            if (k1 && (i^j)<kk) continue;
            pii tmp=dfs(len+1,n1&&(i==nn),m1&&(j==mm),k1&&((i^j)==kk));
            add(f[len][n1][m1][k1].F,tmp.F);
            add(f[len][n1][m1][k1].S,((1ll<<mx-len)*(i^j)%p*tmp.F%p+tmp.S)%p);
        }
    return f[len][n1][m1][k1];
}
int main(){
    scanf("%d",&T);
    while (T--){
        memset(vis,0,sizeof(vis));
        memset(f,0,sizeof(f));
        scanf("%lld%lld%lld%d",&n,&m,&k,&p);
        mx=0;n--;m--;
        count(n);count(m);count(k);
        ans=dfs(1,1,1,1);
        printf("%lld\n",(ans.S%p-k%p*ans.F%p+p)%p);
    }
}

猜你喜欢

转载自blog.csdn.net/xumingyang0/article/details/80098775
今日推荐