ZCC loves strings
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 262144/131072 K (Java/Others)
Total Submission(s): 286 Accepted Submission(s): 108
Problem Description
ZCC has got N strings. He is now playing a game with Miss G.. ZCC will pick up two strings among those N strings randomly(A string can‘t be chosen twice). Each string has the same probability to be chosen. Then ZCC and Miss G. play in turns. Miss G. always
plays first. In each turn, the player can choose operation A or B.
Operation A: choose a non-empty string between two strings, and delete a single letter at the end of the string.
Operation B: When two strings are the same and not empty, empty both two strings.
The player who can‘t choose a valid operation loses the game.
ZCC wants to know what the probability of losing the game(i.e. Miss G. wins the game) is.
Input
The first line contains an integer T(T≤5) which
denotes the number of test cases.
For each test case, there is an integer N(2≤N≤20000) in
the first line. In the next N lines, there is a single string which only contains lowercase letters. It‘s guaranteed that the total length of strings will not exceed 200000.
Output
For each test case, output an irreducible fraction "p/q" which is the answer. If the answer equals to 1, output "1/1" while output "0/1" when the answer is 0.
Sample Input
1 3 xllendone xllendthree xllendfour
Sample Output
2/3
官方题解
结论:对于串a和b,游戏中先手必胜当且仅当|a|+|b|为奇数或a=b. 我们按|a|+|b|的大小从小到大考虑所有的情况。 当|a|+|b|=0时,显然先手必败,符合结论。 假设已经证明了|a|+|b|=k(k<p)的所有情况满足结论,现在考虑|a|+|b|=p的情况。 若p是奇数,先手只需要选择长度较短的不为空的串,并使用A操作,就可以转移到|a|+|b|为偶数并且两个串不相等或者两个串均为空的情况,这种情况先手必败,故此时先手必胜。 若p是偶数,如果两个串相等,显然先手只需要选择使用B操作就能获得胜利了。否则,无论先手如何操作,都只能转移到|a|+|b|为奇数的先手必胜的情况。故此时先手必败。 因此,按顺序考虑每一个串,求得在其之前出现的串中,长度奇偶性与其不同的串共有x个,与其完全相同的串有y个,则对答案有x+y的贡献。累加即可。
PS:游戏者都是按照最优策略操作的,所以当两个字符串的和为奇数时,先手只需每次都先取字符串较短的字符
串,就不会出现相同的字符串,所以两个字符串的和为奇数时,先手必胜。统计奇偶与其不同的只需记录一
前面有多少与其不同的就可,与其相同的字符串用map来统计与其相同的字符串出现多少次,一个出现x次
的字符串,对答案的贡献是x*(x-1)/2
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <map> #include <algorithm> using namespace std; map<string,int> mp; int gcd(int n,int m) { if(m==0) return n; else return gcd(m,n%m); } int main() { int t; scanf("%d",&t); while(t--) { mp.clear(); int n; scanf("%d",&n); int odd=0,even=0; string s; int ans=0; for(int i=0;i<n;i++) { cin>>s; mp[s]++; if(s.size()%2) { odd++; ans+=even; } else { even++; ans+=odd; } } map<string,int>::iterator it; for(it=mp.begin();it!=mp.end();it++) { int cur=it->second; ans+=(cur*(cur-1))/2; } int all=n*(n-1)/2; int gd=gcd(ans,all); printf("%d/%d\n",ans/gd,all/gd); } return 0; }