URAL 1747 Kingdom Inspection 容斥原理

题目大意:

从前有一个王国有着n个城镇,其中国王住在首都里(首都只有1个),现在国王想要从首都出发,经过每个其他城市正好两次后回到首都,问有多少种走法

对于任意的两个城市,都有一条双向的路相连


大致思路:

由于任意两个城市都有路相连,整个地图就是一个有着n个顶点的完全图,我们假设从编号为1的顶点处发后回到顶点,那么真个路线就是2*n个数的排列

其中首尾都是1,中间2,3,4.... n等都是2个,

那么有多少种不同的路线的问题,就是给出数字 编号2 到 n 的点各有两个,能组成多少种不同的排列的问题

那么就是2*k个物品, 分别是1,2,3,4...到k各2个,一共有多少种不同的排列( k = n - 1, 对应的便是原题的答案)


假设条件Si 表示编号为i 的两个数在一起(相邻)

那么答案就是Card((!S1) && ( !S2 ) && ( !S3 ) && ...  && (!Sk) )

也就是 A(2*k , 2*k) - Card(S1 || S2 || S3 || S4 || ...  || Sk)

根据容斥原理可以知道:



这里就涉及到一个问题,由于原题要求取模输出,考虑到分母的2^(k - i), 这里需要做一点处理



这里的一个技巧就是,i*(2*k - i + 1) / 2这个式子中, i 和 (2*k - i + 1) 之中一定会有一个数能够整除2,那么这样就可以整体取模了

这样直接跑一下循环求出 h[ i ]即可


接下来是代码:

Result  :  Accepted     Memory  :  1947 KB     Time  :  31 ms

/*
 * Author: Gatevin
 * Created Time:  2014/7/25 16:42:08
 * File Name: test.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;

#define maxn 1000100


lint n,mod;
lint f[maxn];
lint g[maxn];

int main()
{
    cin>>n>>mod;
    lint k = n - 1;
    f[0] = 1;
    g[k] = 1;
    for(int i = 1; i <= k; i++)
    {
        f[i] = f[i - 1]*(k - i + 1) % mod;
    }
    for(int i = k; i >= 1; i--)
    {
        g[i - 1] = (g[i]*((2*k - i + 1)*i/2)%mod) % mod;
    }
    lint answer = 0;
    for(int i = 0; i <= k; i++)
    {
        if(i & 1)
            answer = (answer - (f[i]*g[i]) % mod + mod) % mod;
        else
            answer = (answer + (f[i]*g[i]) % mod + mod) % mod;
    }
    cout<<answer<<endl;
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值