题目描述
输入格式
输出格式
输入输出样例
输入 #1复制
2 3 1 2 3 4 1 2 3
输出 #1复制
Case 1: 1.0 Case 2: 3.5
一看网上的题解,为什么都是\mathcal{O}(2^n)O(2n)的啊,为什么暴力都能过啊。
先声明一下,下面说的复杂度多不太准确,比如说\mathcal{O}(2^n)O(2n),实际上可能说的是\mathcal{O}(2^n\times n)O(2n×n)或是\mathcal{O}(2^n\times n^2)O(2n×n2),但考虑到这道题对于这种算法的复杂度数据范围放的很宽,且nn很小,这里就忽略不计了,反正这是实现的问题。
然后开始自闭。
结果发现udebug上那份std
好像是可以过30 0
这组数据的。
于是就大概搞了个复杂度似乎有点真的算法。
先说一下那个\mathcal{O}(2^n)O(2n)的做法吧,我们先考虑把联通的点缩起来,我们姑且称之为团,这样问题就转化成了期望需要走多少次才能遍历所有的团。
然后考虑令f_{i,j}fi,j表示现在在第ii个团,选取团的状态为jj,直接dp
即可,如果直接计搜然后用hash
或是map
存状态,这道题就直接过掉了。
然后我们发现其实我们只关心每个团的大小而不关心每个团的具体标号,于是我们可以用f_{i,j}fi,j表示当前在第ii个点,还有jj(jj是一个multiset
,当然也可以把这个multiset
哈希掉)这些团的期望,然后转移。
转移的话大概就是枚举下一步到那个团,直接计搜下去即可,具体可以看代码。
复杂度?我们发现状态大概是把nn划分成多个数字之和的方案数,也就是划分数,n=30n=30时才50005000。
大概跑的是比较快的,极限数据(100100个30 0
)大概只需要跑0.30.3秒(本机)。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
// read/write的代码略去
const int maxn = 305;
int n, m;
struct ZT {
static const int mod = 19260817;
multiset<int> st;
int hsh, xx;
inline void init() {
hsh = xx;
for (auto x : st) {
hsh = ((LL) hsh * hsh % mod * hsh % mod + x) % mod;
}
}
ZT () {
hsh = 0, xx = 0;
st.clear();
}
};
int siz[maxn];
int fa[maxn];
inline int getfa(int x) {
return fa[x] == x ? x : fa[x] = getfa(fa[x]);
}
inline void merge(int x, int y) {
int fax = getfa(x);
int fay = getfa(y);
if (fax != fay) {
if (fax > fay) {
swap(fax, fay);
}
siz[fax] += siz[fay];
fa[fay] = fax;
}
}
map<int, double> mp;
inline double dfs(const ZT& now) {
if (now.st.empty()) {
return 0;
}
if (mp.count(now.hsh)) {
return mp[now.hsh];
}
double ans = 0;
for (auto x : now.st) {
ZT tmp = now;
auto it = tmp.st.find(x);
tmp.xx += x;
tmp.st.erase(it);
tmp.init();
ans += dfs(tmp) * x;
}
ans /= n - 1;
ans++;
ans /= 1. - (double) (now.xx - 1) / (double) (n - 1);
return mp[now.hsh] = ans;
}
inline double solve() {
mp.clear();
read(n), read(m);
for (int i = 1; i <= n; ++i) {
fa[i] = i;
siz[i] = 1;
}
for (int i = 1; i <= m; ++i) {
int x, y;
read(x), read(y);
merge(x, y);
}
ZT fir;
fir.xx = siz[1];
for (int i = 2; i <= n; ++i) {
if (fa[i] == i) {
fir.st.insert(siz[i]);
}
}
fir.init();
return dfs(fir);
}
int main() {
int T;
read(T);
for (int i = 1; i <= T; ++i) {
printf("Case %d: %.10f\n", i, solve());
}
return 0;
}