題面中文,不予翻譯:SDOI2016儲能表php
聽說有大爺用一些奇怪的方法切掉了這道題%%%%%
c++
這裏用的是大衆方法——動態規劃。數組
其實這是一道相似於二進制數位dp的動態規劃題,(可是實際上還不是特別典型的數位dp)這裏就要咱們對問題的深刻理解。ide
若是咱們按照思路進程來發展的話,首先,咱們會想到把求和式和k拆開,先求出全部大於k的(i^j)的和,而後減去若干個k。spa
咱們怎樣去數這個數呢?翻譯
逐位分析,用數位dp的手段,要判斷i是否卡n的上界,j是否卡m的上界,以及i^j是否卡k這個下界,咱們須要統計兩個東西。code
1. 合法的,對答案有貢獻的方案數
blog
2. 合法的,對答案有貢獻的方案的總貢獻。
進程
因此咱們須要dp兩個數組,f[len][1/0][1/0][1/0]表示考慮的第len位是否卡n的第len位(上界),是否卡m的第len位(上界),是否卡k的第len位(下界)時的方案數,和g[len][1/0][1/0][1/0]表示的是相應狀態的總貢獻。get
那麼咱們按照數位dp的思路去作記憶化搜索,最後的答案就是
ans = g[1][1][1][1] - k* f[1][1][1][1]. (固然記得取模)
代碼:
1 #include<bits/stdc++.h> 2 #define ll long long 3 #define pi pair<int,int> 4 #define mp(x,y) make_pair(x,y) 5 using namespace std; 6 const int N=70; 7 bool vis[N][2][2][2];int t,mod,ml; 8 ll n,m,nn,mm,k,kk;pi f[N][2][2][2]; 9 void add(int &x,int y){ 10 x+=y;if(x>=mod) x-=mod; 11 } pi dp(int len,bool n1,bool m1,bool k1){ 12 if(len>ml) return mp(1,0); 13 if(vis[len][n1][m1][k1]) 14 return f[len][n1][m1][k1]; 15 vis[len][n1][m1][k1]=1; 16 int np=(n>>ml-len)&1,mp=(m>>ml-len)&1, 17 kp=(k>>ml-len)&1; 18 for(int i=0;i<=(n1?np:1);i++) 19 for(int j=0;j<=(m1?mp:1);j++){ 20 if(k1&&(i^j)<kp) continue; 21 pi nw=dp(len+1,n1&&(i==np), 22 m1&&(j==mp),k1&&((i^j)==kp)); 23 add(f[len][n1][m1][k1].first,nw.first); 24 add(f[len][n1][m1][k1].second, 25 ((1ll<<ml-len)*(i^j)%mod* 26 nw.first+nw.second)%mod); 27 } return f[len][n1][m1][k1]; 28 } int main(){ 29 scanf("%d",&t);while(t--){ 30 memset(vis,0,sizeof(vis)); 31 memset(f,0,sizeof(f)); 32 scanf("%lld%lld%lld%d",&n,&m,&k,&mod); 33 n--;m--;int nw=0;nn=n,mm=m,kk=k; 34 while(nn) nw++,nn/=2; 35 ml=max(nw,ml);nw=0; 36 while(mm) nw++,mm/=2; 37 ml=max(nw,ml);nw=0; 38 while(kk) nw++,kk/=2; 39 ml=max(nw,ml);pi ans=dp(1,1,1,1); 40 printf("%d\n",(1ll*ans.second-1ll*k%mod* 41 ans.first%mod+mod)%mod); 42 } return 0; 43 }