zoukankan      html  css  js  c++  java
  • P2606 ZJOI2010 排列计数

    [gcd(a,p)=1~~a^{phi(p)} equiv1 mod(p)\ 对于任意bgephi(p),有a^bequiv a^{b~mod~phi(p)+phi(p)}\ b<phi(p),a^bequiv a^{b~mod~phi(n)}(mod~p)\ a和p可以不互质,这里的指数显然满足1\ ]

    P2606 ZJOI2010 排列计数

    由题意(p_i>plfloor i/2 floor),(p_i>p_{2i}(lle n/2)) (p_i>p_{2i+1}(i<n/2))

    显然是个小根堆,在树中填写数字

    假设我们在(i)点,考虑剩下(i-1)个点,容易想到(i-1)个节点选(l)个节点作为左子树,剩下(r)个节点做右子树

    (f[i]={i-1choose l}*f[l]*f[r])

    #include<cstdio>
    #define maxn 1000005
    #define int long long
    using namespace std;
    int a[maxn],n,f[maxn],jc[maxn],m;
    int qpow(int a,int b){
    	int ans = 1;
    	while(b){
    		if(b & 1) ans = ans * a % m;
    		b >>= 1;
    		a = a * a % m;
    	}
    	return ans;
    }
    int ask(int k,int l){
    	if(k == 1||k == 0||k == 2) return 1;
    	if(k == 3) return 2;
    	int u = a[l<<1],v = a[l<<1|1];
    	if(f[u] == 0) f[u] = ask(u,l<<1);
    	if(f[v] == 0) f[v] = ask(v,l<<1|1);
    	return jc[k-1] * qpow(jc[u],m-2) % m * qpow(jc[k-1-u],m-2) % m * f[u] % m * f[v] % m;
    }
    signed main(){
    	scanf("%lld%lld",&n,&m);
    	for(int i = 1;i <= n;i++){
    		int k = i;
    		while(k) a[k] ++,k>>=1;
    	}
    	jc[0] = jc[1] = 1;
    	for(int i = 2;i<=n;i++)	jc[i] = jc[i-1] * i % m;
    	printf("%lld",ask(n,1));
    }
    

    更妙的是

    答案等价于求一个树的拓扑排序数量

    数字的大小关系可以看做是一条有向边,这样以每个位置当点,就可以把整个排列当做一张有向图。而且题目保证有解,所以只一张有向无环图。这样子,我们就可以把排列计数的问题转化为一个图的拓扑排序计数问题

    [公式ans=frac{n!}{prod_{i=1}^ns[i]}~~~s[i]为子节点的数量\ f[u]=frac{(s[u]-1)!*prod f[v]!}{prod s[v]!}\ ]

  • 相关阅读:
    java几种常用设计模式简单示例
    字符串常用方法
    File类常用方法
    如何在一个form表单中实现多个submit
    CC EAL认证
    ARQC与ARPC的生成和校验方法
    金融卡IC卡知识50问
    M(必备),R(需求),C(条件),O(可选)
    非接触IC卡中typeA卡和typeB卡的区别--总结,二者的调制方式和编码方式不同
    电子钱包和电子存折的区别
  • 原文地址:https://www.cnblogs.com/shikeyu/p/13365688.html
Copyright © 2011-2022 走看看