1. 程式人生 > 其它 >第 45 屆國際大學生程式設計競賽(ICPC)亞洲區域賽(濟南)L Bit Sequence —— 記憶化搜尋

第 45 屆國際大學生程式設計競賽(ICPC)亞洲區域賽(濟南)L Bit Sequence —— 記憶化搜尋

技術標籤:想法dpdfs

This way

題意:

定義f(x)表示x的二進位制上1的個數
給你m個數,問你有多少個數在0~L之間,滿足f(x+i)=a[i](0<=i<m)

題解:

我L都寫出來了…我還是沒有拿金
那麼可以猜測到這是一個記憶化搜尋
列舉最後被m影響的幾位,然後記憶化前面的部分。
因為會有進位,所以需要特殊地考慮。
ad表示非進位的時候需要的前面的位數是奇數還是偶數,adf表示進位後需要奇數還是偶數。
由於會有進位那麼我們需要知道進位影響了多少個1
dp[i][j][k]表示到了第i位,連續最後1的個數是j個,當前有奇數個1還是偶數個1
那麼到了邊界的時候,是否進位需要分別考慮

在這裡插入圖片描述
上面是0的情況,下面是1的情況
並且到了上限的時候,也需要考慮非進位能否滿足條件:
在這裡插入圖片描述
上面表示只能取0。
做完之後我們可以發現,其實在列舉最後幾位的時候,去dfs會超過L的限制,比如L是
100001010
那麼比如我們列舉的是最後4位,在情況為1111的時候,前面的位數照常列舉,所以最大會舉到
100001111,超過了L
因此需要for一遍將額外的值刪掉。同時額外的值不會超過2^7,可以直接暴力列舉
在這裡插入圖片描述
這個就是檢視一個長整型二進位制有多少個1。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=
65,M=105; ll dp[N][N][2]; int a[M],f,b[N],ad,adf,low; ll dfs(int pos,int len,int even,int lim){ if(pos==low){ ll ans=0; if(f){ if(ad==even&&adf==even)ans++; if(!lim||(lim&&b[pos])){ if(adf==((even+len)&1)&&ad==((even+
1)&1)) ans++; } } else{ if(lim&&b[pos]==0){ if(ad==even)ans=1; } else ans=1; } return ans; } if(!lim&&~dp[pos][len][even])return dp[pos][len][even]; ll ans=0; int top=lim?b[pos]:1; for(int i=0;i<=top;i++) ans+=dfs(pos-1,i==0?0:len+1,(even+i)&1,lim&&i==top); if(!lim) dp[pos][len][even]=ans; return ans; } int main() { int t; scanf("%d",&t); while(t--){ int m; ll l; scanf("%d%lld",&m,&l); for(int i=1;i<=m;i++)scanf("%d",&a[i]); low=0; int vv=m; while(vv)low++,vv>>=1; int mx=(1<<low)-1; ll ans=0; for(int i=0;i<=mx;i++){ if(i>l)break; ad=(a[1]^__builtin_popcount(i))&1; adf=2; for(int j=2;j<=m;j++){ int v=(a[j]^__builtin_popcount(i+j-1))&1; if(i+j-1>mx){ if(adf==2)adf=v; if(adf!=v){ adf=-1; break; } continue; } if(v!=ad){ ad=-1; break; } } if(ad==-1||adf==-1)continue; if(mx>=l){ if(!ad) ans++; continue; } if(i+m-1>mx)f=1; else f=0; memset(dp,-1,sizeof(dp)); int top=0; ll x=l; while(x)top++,x>>=1; top--; for(int j=top;~j;j--) b[j]=(l>>j)&1; ans+=dfs(top,0,0,1); } if(mx<l){ ll v=l|mx; //v--; while(v>l){ int del=0; for(int i=1;i<=m;i++){ ll now=v+i-1; int num=__builtin_popcount(now); ll mm=1ll<<32; if(now>mm){ now>>=32; num+=__builtin_popcount(now); } if(num%2!=a[i]){ del=1; break; } } if(!del)ans--; v--; } } printf("%lld\n",ans); } return 0; }