题意
ZCC有N个字符串,他正在和Miss G.用这N个字符串玩一个小游戏。ZCC会从这N个串中等概率随机选两个字符串(不可以是同一个)。然后,ZCC和Miss G.轮流操作。Miss G.总是先操作的。在每轮中,操作者可以选择操作A或操作B。
操作A:在两个串中选择一个当前非空的串,然后在这个串的末尾删去一个字符。
操作B: 若当前两个串完全相同且非空,则可以使用这个操作。此时两个串都被清空。
不能操作的玩家输掉了这个游戏。
ZCC想要知道他输掉游戏的概率是多少(也就是Miss G.获胜的概率)。
解析:
首先从n个字符串中选取2个字符串的可能有 C(n,2) 种。
然后给你两个字符串 a,b 肯定存在着先手必胜和先手必败两种状态。
关键是如何确定这两种状态。
引用一下官方题解:结论:对于串a和b,游戏中先手必胜当且仅当|a|+|b|为奇数或a=b。
我们按|a|+|b|的大小从小到大考虑所有的情况。
当|a|+|b|=0时,显然先手必败,符合结论。
假设已经证明了|a|+|b|=k的所有情况满足结论,现在考虑|a|+|b|=p的情况。
若p是奇数,先手只需要选择长度较短的不为空的串,并使用A操作,就可以转移到|a|+|b| 为偶数并且两个串不相等或者两个串均为空的情况,这种情况先手必败,故此时先手必胜。
若p是偶数,如果两个串相等,显然先手只需要选择使用B操作就能获得胜利了。否则,无论先手如何操作,都只能转移到|a|+|b|为奇数的先手必胜的情况。故此时先手必败。
因此,按顺序考虑每一个串,求得在其之前出现的串中,长度奇偶性与其不同的串共有x个,与其完全相同的串有y 个,则对答案有x+y的贡献。累加即可。
AC代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <string>
using namespace std;
typedef long long ll;
const int N = 200005;
map<ll, int> mp;
map<ll, int>::iterator it;
char str[N];
ll Hash(char *str) {
ll sum = 0;
while(*str) {
sum = sum*256 + *(str++);
}
return sum;
}
ll gcd(ll a, ll b) {
if(b == 0) return a;
else return gcd(b, a%b);
}
int main() {
//freopen("in.txt", "r", stdin);
int T, n;
scanf("%d", &T);
while(T--) {
mp.clear();
scanf("%d", &n);
ll ans = 0, odd = 0, even = 0, equal = 0;
for(int i = 0; i < n; i++) {
scanf("%s", str);
mp[Hash(str)]++;
int len = strlen(str);
if(len & 1) odd++;
else even++;
}
for(it = mp.begin(); it != mp.end(); it++) {
ll tmp = it->second;
equal += tmp*(tmp-1)/2;
}
ll nu = even*odd + equal, de = n*(n-1)/2;
ll g = gcd(nu, de);
printf("%lld/%lld\n", nu/g, de/g);
}
return 0;
}