传送门:https://codeforces.com/contest/1569/problem/F
先考虑合法的字符串数量上界 考虑搜索发现只有 1.5 e 5 1.5e5 1.5e5的量 (也可考虑贝尔数为理论上界状态)
对于一个给定的字符串 如果每个字符只出现两次 我们可以通过状压dp快速解决
O
(
2
n
/
2
∗
(
n
/
2
)
2
)
O(2^{n/2}*(n/2)^2)
O(2n/2∗(n/2)2)
并且满足上述条件的字符串很少(
1
e
5
1e5
1e5)左右 那么复杂度是可以接受的
然后对于每个字符出现多次,那么可以等价到 2 2 2 次的情况 总状态数不超过贝尔数 (实际上还要再小一点) 可以用 m a p map map 实现记忆化
PS:对于这类数列/字符串循环等价的情况 可以用贝尔数/搜索来估算状态上界
#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define ll long long
#define pb push_back
#define P pair<int,int>
#define fr first
#define se second
#define mp make_pair
int rd() {
int sum = 0;char c = getchar();bool flag = true;
while(c < '0' || c > '9') {if(c == '-') flag = false;c = getchar();}
while(c >= '0' && c <= '9') sum = sum * 10 + c - 48,c = getchar();
if(flag) return sum;
else return -sum;
}
int n,m,k,kk;
int pos1[15],pos2[15],fac[15];
bool f[15][15];
int num[15];
unordered_map<ll,bool>dp;
map<pair<int,int>,bool>dp1;
vector<int>tmp,gg;
int cnt;
void solve() {
rep(i,1,n) {
pos1[tmp[i-1]] = pos2[tmp[i-1]];
pos2[tmp[i-1]] = i;
}
rep(i,1,n/2) if(f[pos1[i]][pos2[i]]) dp1[ mp( 1<<(i-1) , i ) ] = true;
rep(s,0,(1<<(n/2))-1) rep(i,1,n/2) if(dp1[ mp( s , i )]) {
rep(j,1,n/2) if( ( s & (1<<(j-1)) ) == 0 )
if( (f[pos1[i]][pos1[j]] && f[pos2[i]][pos2[j]]) ||
(f[pos1[i]][pos2[j]] && f[pos2[i]][pos1[j]]) )
dp1[ mp( s+(1<<(j-1)) , j ) ] = true;
}
ll res = 0;
rep(i,1,n) {
res *= 6;
res += tmp[i-1]-1;
}
rep(i,1,n/2) if(dp1[ mp( (1<<(n/2))-1 , i ) ] ) {
dp[res] = true;
break;
}
if(!dp[res]) dp[res] = 0;
dp1.clear();
}//复杂度还可以降一个阶乘
void pre(int x) {
if(x == n/2+1) {
solve();
return;
}
rep(i,1,n) if(!tmp[i-1]){
tmp[i-1] = x;
rep(j,i+1,n) if(!tmp[j-1]) {
tmp[j-1] = x;
pre(x+1);
tmp[j-1] = 0;
}
tmp[i-1] = 0;
break;
}
return;
}
int id[20];
bool dfss(int cnt) {
rep(i,0,cnt-1) id[i] = -1;
int id_num = -1;
rep(i,0,n-1) if(id[tmp[i]] == -1) id[tmp[i]] = ++id_num;
ll s = 0;
rep(i,0,n-1) {
s *= 6;
s += id[tmp[i]];
}
if(dp.find(s) != dp.end() || cnt == n/2) return dp[s];
rep(i,0,n-1) if(num[tmp[i]] > 2) {
rep(j,i+1,n-1) if(tmp[i] == tmp[j]) {
int t = tmp[i]; num[t] -= 2;
tmp[i] = cnt; tmp[j] = cnt; cnt++;
if(dfss(cnt)) {
cnt--; tmp[i] = t; tmp[j] = t; num[t] += 2;
dp[s] = true;
break;
}
cnt--; tmp[i] = t; tmp[j] = t; num[t] += 2;
}
// break; //很重要?的剪枝 o实际上并不
}
return dp[s];
}
void dfs(int x) {
if(x == n) {
rep(i,1,kk) if((num[i-1]&1) || num[i-1] == 0) return;
dfss(kk);
return;
}
rep(i,1,kk) {
num[i-1]++; tmp[x] = i-1;
dfs(x+1);
num[i-1]--; tmp[x] = -1;
if(num[i-1] == 0) break;
}
return;
}
unordered_map<ll,bool>::iterator it;
int main() {
n = rd(); m = rd();k = rd();
rep(i,1,m) {
int x = rd(),y = rd();
f[x][y] = f[y][x] = true;
}
rep(i,1,n) tmp.pb(0);
pre(1);
fac[0] = 1; rep(i,1,12) fac[i] = fac[i-1]*i;
for(kk = 1;kk <= min(k,n/2);++kk) {
rep(i,1,n) tmp[i-1] = -1;
dfs(0);
}
ll ans = 0;
for(it = dp.begin();it != dp.end();it++) if(it->second) {
ll res = it->fr; int mx = 0;
while(res) {mx = max( (int)(res%6) , mx );res /= 6;}
mx++;
if(mx <= k) ans += fac[k] / fac[k-mx];
}
printf("%lld\n",ans);
return 0;
}