第 45 屆國際大學生程式設計競賽(ICPC)亞洲區域賽(濟南)L Bit Sequence —— 記憶化搜尋
阿新 • • 發佈:2020-12-28
題意:
定義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;
}