[CodeForces 285D Permutation Sum] (搜索)

题目链接:http://codeforces.com/problemset/problem/285/D


题目大意:

有序列 a,b。其中长度均为N,而a1,a2,a3.....an各不相同,且都属于[1, n]。b也是。

现在有一个操作,ci = ((ai - 1 + bi - 1) mod n) + 1 (1 ≤ i ≤ n). 从而生成一个新的序列C。且使得C也符合上述序列的要求。

问a,b有多少种不同的选择,当a!=b时,(a,b)(b,a)为两种选择。


解题思路:

只能够想到n * n!的方法,而且通过打表可以偶数时为0,但奇数最大为15,15!* 15 太大了。

网上看到一个复杂度 为(2^n) * (2^n) 的搜索。虽然也无法在3s内搜完,但至少能打出表。。= =!


其实只需要找到1,2,3,4,……n有多少种匹配的b序列,之后将答案乘以n!(n的全排列)即可。

对于n,用[0, 1<<n)(即二进制枚举)来表示,然后分为两部分搜索。

前一部分选取n1个数字,然后用(1,2,3,……n1)这些数字作为b序列,后一部分选取n-n1个数,然后用(n2,n3……n)作为b序列(在搜索过程中考虑了排列问题)

两者sum1[j] * sum2[j ^ ((1 << n) - 1]即为该种情况的答案数。

/*
ID: wuqi9395@126.com
PROG:
LANG: C++
*/
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<string>
#include<fstream>
#include<cstring>
#include<ctype.h>
#include<iostream>
#include<algorithm>
#define INF (1<<30)
#define PI acos(-1.0)
#define mem(a, b) memset(a, b, sizeof(a))
#define For(i, n) for (int i = 0; i < n; i++)
const int MOD = 1000000007;
typedef long long ll;
int n;
int n1, n2;
int g1[20], g2[20];
int key;
int cnt1, cnt2;
ll sum1[1 << 17], sum2[1 << 17];

int pan(int i, int k) {
    int sum = 0;
    while(i) {
        sum += i & 1;
        i = i >> 1;
    }
    return sum == k;
}

void dfs1(int s) {
    if(s > n1) {
        sum1[key]++;
        return ;
    }
    for(int i = 0; i < cnt1; i++) {
        int tmp = g1[i];
        if(g1[i] == -1) continue;
        int tmp1 = (tmp + s - 2) % n + 1;
        if(key & ( 1 << (tmp1 - 1) ) ) continue;
        key |= ( 1 << (tmp1 - 1) );
        g1[i] = -1;
        dfs1(s + 1);
        g1[i] = tmp;
        key ^= ( 1 << (tmp1 - 1) );
    }
}
void dfs2(int s) {
    if(s > n) {
        sum2[key]++;
        return ;
    }
    for(int i = 0; i < cnt2; i++) {
        int tmp = g2[i];
        if(g2[i] == -1) continue;
        int tmp1 = (tmp + s - 2) % n + 1;
        if(key & ( 1 << (tmp1 - 1) ) ) continue;
        key |= ( 1 << (tmp1 - 1) );
        g2[i] = -1;
        dfs2(s + 1);
        g2[i] = tmp;
        key ^= ( 1 << (tmp1 - 1) );
    }
}
int main() {
    for(int i = 1; i <= 16; i++) {
        n = i;
        n1 = n / 2;
        n2 = n - n1;
        ll ans = 0;
        for(int i = 0; i <= (1 << n) - 1; i++) {
            if(pan(i, n1) == 0) continue;
            cnt1 = 0;
            cnt2 = 0;
            for(int j = 0; j < n; j++) {
                if( (1 << j)&i ) g1[cnt1++] = j + 1;
                else g2[cnt2++] = j + 1;
            }
            memset(sum1, 0, sizeof(sum1));
            memset(sum2, 0, sizeof(sum2));
            key = 0; // 主要用来记录c的情况...
            dfs1(1);
            key = 0;
            dfs2(n1 + 1);
            for(int j = 0; j <= (1 << n) - 1; j++) {
                int tmp = j ^ ((1 << n) - 1);
                ans = (ans + (sum1[j] * sum2[tmp]) % MOD) % MOD;
            }
        }
        for(int i = 1; i <= n; i++)
            ans = (ans * i) % MOD;
        printf("%I64d,", ans);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值