BZOJ 4513: [Sdoi2016]储能表(记忆化搜索)
2018-03-14 15:10
218 查看
题目
原题链接格子(i,j)的大小是max(i^j-k,0),求一个前缀矩阵mod p(动态给出)的值,范围可达1e18,5000组询问。
分析
网上搜链接,握草,只写一个状态!?网上搜代码,握草,全是填表法!?
还有什么叫直接DP就好了,小生完全不会啊啊啊啊啊!!!!
还有这道题好像可以找规律,然后我数位DP分析着分析着就变成找规律了,而且还很疑惑觉得为什么和周围的小伙伴不一样?要注意数位DP的内涵啊。
切入正题:
由于是xor运算,很容易想到转换成二进制来做。
我们实际上要做的就是要找到这样的一些点对的亦或和:i<=n && j<=m && i^j>=k,然后从高位开始考虑,因此记忆化搜索的时候需要同时把n,m,k转换成二进制后带入限制,其中这里k是向下的限制,所以和一般的数位DP略有不同。
在转移的时候,我们考虑的是最高位可以选择的情况,正如一般的数位DP一样,但是我们没有必要并且并不容易把两个元素的同一位分开考虑,所以对于一个状态我们同事考虑两个元素pos的选择。
所以我们在记忆化的时候用f(i,lim1,lim2,lim3)表示当前第i位,n,m,k是否有限制时的前缀矩阵和。
推到后面你发现推不下去了,所以自然而然加上一个表示i^j>=k的元素的个数。就可以顺利递推了。
可是为什么网上这么多人选择直接递推呢?因为不同于一般的数位DP,这里同一个限制状态可能被多个不同的转移调用,不记录有限制的时候的状态会T,所以这次我们要记忆转移的所有状态,包括限制情况。这么一来,所有状态都被记录,没有任何必要用记忆化了,不过这种要记录限制的情况只能对一个数使用,因为不同的数限制不同。
不过我还是写了记忆化来填补一下网上题解没有记忆化的遗憾嘛。
代码
两个DP可以通过传值变量合成一个,写成两个方便理解,上面是个数计算,下面是和的计算。注意每次分解数组要清空。
有很多爆int的地方要小心。
#include<cmath> #include<queue> #include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef long long LL; const int maxn=65; int T,p; LL n,m,k; LL f[maxn][2][2][2],g[maxn][2][2][2]; int z1[maxn],z2[maxn],z3[maxn]; LL dp1(int pos,bool lim 4000 1,bool lim2,bool lim3) { if(pos<1)return 1; if(g[pos][lim1][lim2][lim3]!=-1)return g[pos][lim1][lim2][lim3]; int up1=lim1?z1[pos]:1; int up2=lim2?z2[pos]:1; int down=lim3?z3[pos]:0; bool l1,l2,l3; LL ret=0; for(int a=0;a<=up1;a++) { for(int b=0;b<=up2;b++) { if((a^b)<down)continue; l1=lim1 && a==up1; l2=lim2 && b==up2; l3=lim3 && (a^b)==down; ret=(ret+dp1(pos-1,l1,l2,l3))%p; } } return g[pos][lim1][lim2][lim3]=ret; } LL dp2(int pos,bool lim1,bool lim2,bool lim3) { if(pos<1)return 0; if(f[pos][lim1][lim2][lim3]!=-1)return f[pos][lim1][lim2][lim3];; int up1=lim1?z1[pos]:1; int up2=lim2?z2[pos]:1; int down=lim3?z3[pos]:0; bool l1,l2,l3; LL ret=0; for(int a=0;a<=up1;a++) { for(int b=0;b<=up2;b++) { if((a^b)<down)continue; l1=lim1 && a==up1; l2=lim2 && b==up2; l3=lim3 && (a^b)==down; ret=(ret+dp2(pos-1,l1,l2,l3))%p; if(a^b)ret=( ret + ( ( 1ll << ( pos-1 ) ) %p ) * dp1(pos-1,l1,l2,l3) )%p; } } return f[pos][lim1][lim2][lim3]=ret; } void fenjie(LL x,int *z) { int sz=0; do{ z[++sz]=x&1; x>>=1; }while(x); } void calc() { scanf("%lld%lld%lld%d",&n,&m,&k,&p); n--,m--; memset(f,-1,sizeof(f)); memset(g,-1,sizeof(g)); memset(z1,0,sizeof(z1)); memset(z2,0,sizeof(z2)); memset(z3,0,sizeof(z3)); fenjie(n,z1); fenjie(m,z2); fenjie(k,z3); printf("%lld\n",(dp2(61,1,1,1)-1ll*k%p*dp1(61,1,1,1)%p+p)%p); } int main() { scanf("%d",&T); while(T--) calc(); return 0; }
相关文章推荐
- [bzoj4513][SDOI2016]储能表
- bzoj 4513: [Sdoi2016]储能表 数位dp
- BZOJ 4513 [SDOI2016]储能表 【数位DP】
- BZOJ 4513 [Sdoi2016]储能表
- BZOJ 4513: [Sdoi2016]储能表 数位dp
- [BZOJ4513] [SDOI2016] 储能表 - 数位DP
- [bzoj4513][SDOI2016]储能表——数位dp
- 搜索(四分树):BZOJ 4513 [SDOI2016 Round1] 储能表
- 【BZOJ4513】[Sdoi2016]储能表 数位DP
- bzoj千题计划277:bzoj4513: [Sdoi2016]储能表
- BZOJ 4513: [Sdoi2016]储能表 [数位DP !]
- 【BZOJ4513】【Sdoi2016】储能表
- BZOJ4513: [Sdoi2016]储能表
- bzoj 4513: [Sdoi2016]储能表
- [BZOJ4513][SDOI2016]储能表(数位DP/分治乱搞)
- [BZOJ 4513 ~ 4518] SDOI 2016 R1 day1 + day2 口胡题解
- 【bzoj4513】[Sdoi2016]储能表 数位dp
- 4513: [Sdoi2016]储能表
- 4513: [Sdoi2016]储能表
- 【BZOJ 4513】【SDOI 2016】储能表