题意:给出n个长度<=11的只含有012的串,定义2个串的距离为所有对应位相减的绝对值的和。求距离0到2×m-1的对数。
做法:挺难想的一个状压dp。只要把跟每个串的距离为x的有多少个算出来就可以求出最后的答案了。考虑一下子问题,我们只考虑与后几位的距离为x的有多少个,但是这样没法递推,因为前面的不知道,所以我们可以把前面的补全,故就有了dp[s1][s2][k]代表一些串的前缀就是s1,与s1+s2这个串的距离差为k的个数。我们可以发觉|s1|+|s2| = m,所以我们可以把前2维变成1维。
如果用刷表法就不用像题解填表法那样有9种情况了,只有3种情况,把第i位当作0,1,2即可。
假设第i位是1,转移就是
1.dp[s1-'1']['0'+s2][k+1] += dp[s1][s2][k].
2. dp[s1-'1']['1'+s2][k] += dp[s1][s2][k],
3. dp[s1-'1']['2'+s2][k+1] += dp[s1][s2][k].
真正写的过程中只要把第i位换成0,1,2就可以了。
初始值的设定是dp[s][空][0] = s这个串的个数。
AC代码:
#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<ctype.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdlib>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<string.h>
#include<string>
#include<sstream>
#include<bitset>
using namespace std;
#define ll __int64
#define ull unsigned __int64
#define eps 1e-8
#define NMAX 1000000010
#define MOD 1000000007
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define PI acos(-1)
template<class T>
inline void scan_d(T &ret)
{
char c;
int flag = 0;
ret=0;
while(((c=getchar())<'0'||c>'9')&&c!='-');
if(c == '-')
{
flag = 1;
c = getchar();
}
while(c>='0'&&c<='9') ret=ret*10+(c-'0'),c=getchar();
if(flag) ret = -ret;
}
const int maxn = 90000+10;
int dp[2][200000][25];
int cnt[200000];
char s[20];
int the[20],m;
ll ans[30];
int main()
{
#ifdef GLQ
freopen("input.txt","r",stdin);
// freopen("o1.txt","w",stdout);
#endif
the[0] = 1;
for(int i = 1; i <= 11; i++) the[i] = the[i-1]*3;
int _t;
scanf("%d",&_t);
while(_t--)
{
int n;
scanf("%d%d",&n,&m);
for(int i = 0; i < the[m]; i++) cnt[i] = 0;
for(int i = 0; i < n; i++)
{
scanf("%s",s);
int x = 0;
for(int j = 0; j < m; j++)
x = x*3+s[j]-'0';
cnt[x]++;
}
int f = 0;
for(int i = 0; i < the[m]; i++)
for(int j = 1; j <= 2*m; j++)
dp[0][i][j] = 0;
for(int i = 0; i < the[m]; i++)
dp[0][i][0] = cnt[i];
for(int i = 0; i < m; i++)
{
for(int j = 0; j < the[m]; j++)
for(int k = 0; k <= 2*m; k++) dp[f^1][j][k] = 0;
for(int j = 0; j < the[m]; j++)
for(int k = 0; k <= 2*m; k++) if(dp[f][j][k])
{
int tmp = j/the[i]%3;
for(int l = 0; l <= 2; l++)
{
int x = j-(tmp-l)*the[i];
dp[f^1][x][k+abs(l-tmp)] += dp[f][j][k];
}
}
f ^= 1;
}
memset(ans,0,sizeof(ans));
for(int i = 0; i < the[m]; i++)
{
if(!cnt[i]) continue;
ans[0] += (ll)cnt[i]*(cnt[i]-1);
for(int j = 1; j <= 2*m; j++)
{
ans[j] += (ll)cnt[i]*dp[f][i][j];
}
}
for(int i = 0; i <= 2*m; i++)
printf("%I64d\n",ans[i]/2LL);
}
return 0;
}