codeforces Educational Codeforces Round 113 F. Palindromic Hamil (状压dp)

传送门: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; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值