题目:
注意:
这里的范围是n*m<=1e5!!!
分析:
前面的小数据可以用n^2 * m的暴力过掉(其实后面也可以)
暴力100
#include<bits/stdc++.h> using namespace std; #define N 100005 #define ll long long int m; ll t[N],n; vector<char> s[N]; void work1() { char ch[5]; int a; for(int i=1;i<=n;i++){ scanf("%s",ch); if(ch[0]=='x') a=1; if(ch[0]=='y') a=2; if(ch[0]=='z') a=3; t[a]++; } ll ans=0; for(int i=1;i<=3;i++) ans+=t[i]*(t[i]-1)/2; printf("%lld\n%lld\n",n*(n-1)/2-ans,ans); } void work2() { for(int i=0;i<=m-1;i++) printf("0\n"); printf("%lld\n",n*(n-1)/2); } char tt[N]; int main() { freopen("words.in","r",stdin); freopen("words.out","w",stdout); scanf("%lld%d",&n,&m); if(m==1) { work1(); return 0; } int fl=1; for(int i=1;i<=n;i++){ scanf("%s",tt); for(int j=0;j<m;j++) s[i].push_back(tt[j]); if(i==1) continue; for(int j=0;j<s[i].size();j++) if(s[i][j]!=s[i-1][j]) { fl=0; break; } } if(fl) { work2(); return 0; } for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++){ int cnt=0; for(int k=0;k<=m-1;k++) if(s[i][k]==s[j][k]) cnt++; t[cnt]++; } } for(int i=0;i<=m;i++) printf("%lld\n",t[i]); return 0; } /* 8 1 x y y z y x x x 4 3 xyz xyz zzx xzz 4 4 xyzx xyzx xyzx xyzx */
单独处理数据4和数据5,也比较简单。
但是看了题解发现,对于m较小的点应该用状压dp实现。
状压dp:
有三种字母:x,y,z,所以应该用三进制保存字符串的状态,要求的是相似度一定时的对数,所以定义dp[ s ][ k ]为状态为s,相似度为k的对数。
初始化:dp[ x ][ m ]=s[ x ] s是原串。
转移:dp[ s' ][ k-1 ]+=dp[ s ][ k ] ,s'是改变1个字符后的状态,改变了一个字符,相似度就应该-1。
但是会发现,对于这组数据:xyz,xyy,xyx
dp[ xyy ][ 2 ]+=dp[ xyz ][ 3 ];(枚举xyz时把z改成y去更新xyy相似度为2的方案)
dp[ xyx ][ 1 ]+=dp[ xyy ][ 2 ];(枚举xyy时把y改成x去更新xyx相似度为1的方案)
明显dp[ xyx ][ 1 ]==0 ,但是通过上述顺序更新处理=1。
是哪里出了问题呢?因为我们把xyz的第三位改变了两次,当成了整个串改变了两个位置!!!
所以应该固定每一个串改变的位置(即最外层枚举要修改的位置),并且一个值被改变后,不能在同一层循环里面再去更新别的值。
所以要用一个ff数组临时储存一下再来转移。
代码细节很多。。。
(下面是std,自己并没有打)
void Work1(int n, int m) { for (int i = 1; i <= n; i++) { int now = 0; for (int j = 1; j <= m; j++) { now *= 3; char k; for (k = getchar(); k <= 32; k = getchar()); now += k - 'x'; } ss[now]++; } mi3[0] = 1; for (int i = 1; i <= m; i++) mi3[i] = mi3[i - 1] * 3; for (int i = 0; i < mi3[m]; i++) dp1[i][m] = ss[i]; for (int i = 0; i < m; i++) { memcpy(dp, dp1, sizeof dp); for (int j = 0; j < mi3[m]; j++) { int s1 = j / mi3[i] % 3, s2 = j - s1 * mi3[i]; for (int t = 0; t < 3 * mi3[i]; t += mi3[i]) if (t != s1 * mi3[i]) for (int p = m - i; p <= m; p++) dp[s2 + t][p - 1] += dp1[j][p]; } memcpy(dp1, dp, sizeof dp); } for (int i = 0; i < mi3[m]; i++) for (int p = 0; p <= m; p++) ans[p] += 1LL * ss[i] * dp1[i][p]; ans[m] = ans[m] - n; for (int i = 0; i <= m; i++) ans[i] /= 2; }