ECNU Online Judge P3733 字母游戏

原题链接:https://acm.ecnu.edu.cn/problem/3733/

 

前言:实际上如果当时可以回想起来hash这个事情的话,估计就做出来了,然后就进了前20(笑哭),tcl。今天晚上调了一个晚上,发现改成hash之后到处都是bugQWQ

 

分析:

可以发现由于等价具有传递性,当K>=7的情况下就可以完全不用特别处理了,答案就是C(N,2),因为这个时候所有的字都等价,那么所有的字符串都等价了。

对于K<=7的情况,可以发现实际上我们就是要根据题目K的限制把8个字符划分成若干个集合,集合内的字母等价。根据第二类斯特林数(后面会补一篇相关文章),可以发现总共的有意义状态数(也就是所有的K个等价条件都使用,并且等价条件使用不形成环)大概在5000以内,于是可以考虑一下枚举每种划分。

关于划分的枚举,考虑一个等价条件,实际上可以看成一条边,我们要有意义地利用所有的边,那么就是生成一棵树(或者说森林)。生成森林的话只要在递归过程中枚举每个点的父亲,并且保证父亲对应字符小于当前字符即可。

划分完成后,考虑字符串的对比。暴力对比会T,考虑hash,并优化。对于字符串进行预处理,即对字符串i,把其中的不同字符的hash值拆分开来算,求得i中某个字符变成另外某个字符的时候对应的对串i的hash值的贡献。最后每次生成划分之后比较字符串的时候只要通过O(8)的时间把预处理的内容组合起来就可以得到对应的正确hash值。

每次划分完成之后的计算时间复杂度是O(nlogn),可以通过此题。

 

(久了不怎么做题,这个题思路出来之后细节调了好久,对于字符统一化要找树的根的问题一开始还完全没有意识到)

 

AC代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<algorithm>
 6 #include<cmath>
 7 #include<queue>
 8 #include<set>
 9 #include<map>
10 #include<vector>
11 #include<cctype>
12 using namespace std;
13 typedef long long LL;
14 const LL mo=1e15+13;
15 const int maxn=1005;
16 
17 char S[maxn][maxn]; int N,M,K,ans;
18 int fa[10],rt[10];
19 LL Hash[maxn][10][10],val[maxn];//hash[i][j][k] 表示第i个字符串中字符j转化为字符k的对应hash值 
20 
21 void data_in()
22 {
23     scanf("%d%d%d",&N,&M,&K);
24     for(int i=1;i<=N;i++)
25         scanf("%s",S[i]);
26 }
27 void ready()
28 {
29     for(int i=1;i<=N;i++)
30     for(int k=0;k<8;k++){
31         LL tmp=1;
32         for(int j=0;j<M;j++){
33             int jj=S[i][j]-'a';
34             Hash[i][jj][k]=(Hash[i][jj][k]+tmp*k)%mo;
35             tmp=tmp*29%mo;
36         }
37     }
38 }
39 LL Hash_val(int i)
40 {
41     LL re=0;
42     for(int j=0;j<8;j++)
43         re+=Hash[i][j][rt[j]];
44     return re%mo;
45 }
46 int root(int j)
47 {
48     while(fa[j]!=j) j=fa[j];
49     return j;
50 }
51 void calc()
52 {
53     for(int j=0;j<8;j++) rt[j]=root(j);
54     for(int i=1;i<=N;i++)
55         val[i]=Hash_val(i);
56     sort(val+1,val+N+1);
57     int i=1,re=0;
58     while(i<=N){
59         int cnt=1;
60         while(i<N&&val[i]==val[i+1]) i++,cnt++;
61         re+=cnt*(cnt-1)/2;
62         i++;
63     }
64     if(re>ans) ans=re;
65 }
66 void run(int i,int k)
67 {
68     if(i==8&&k==K){ calc(); return; }
69     if(i==8||k>K) return;
70     fa[i]=i;
71     run(i+1,k);
72     for(int j=0;j<i;j++){
73         fa[i]=j;
74         run(i+1,k+1);
75     }
76 }
77 void work()
78 {
79     fa[0]=0;
80     if(K>=7) ans=N*(N-1)/2;
81     else{
82         ready();
83         run(1,0);
84     }
85     printf("%d\n",ans);
86 }
87 int main()
88 {
89     freopen("test.in","r",stdin);
90     freopen("test.out","w",stdout);
91     data_in();
92     work();
93     return 0;
94 }
View Code

 

猜你喜欢

转载自www.cnblogs.com/Golden-Elf/p/11986053.html