zoukankan      html  css  js  c++  java
  • [AGC005D] ~K Perm Counting [dp]

    题面

    传送门

    思路

    首先可以明确的一点是,本题中出现不满足条件的所有的数,都是分组的

    只有模$K$意义下相同的数之间才会出现不满足条件的情况,而且仅出现在相邻的情况

    那么我们考虑把这个性质利用起来

    我们单独把其中一组抽出来考虑:设这一组为$p,p+k,p+2k,p+3k.....$

    那么我们发现,这其中每两个相邻的数之间都是互相不能选的

    但是要注意,我们本题中讨论的实际上更应该是位置的关系,所以此时就变成了这样的形式:

    位置$p+ki$不能选数$p+k(i+1)$,位置$p+ki(i+1)$不能选数$p+ki$

    也就是说这两条边是交叉的

    我们考虑对于每个值构建两个点,一个代表顺序为这个值的位置,一个代表这个数

    那么上述的同一组的数中,我们连接不能同时选的数和位置,可以构造出两条链:

    数$p$---位置$p+k$---数$p+2k$---位置$p+3k$......

    位置$p$---数$p+k$---位置$p+2k$---数$p+3k$......

    这两条链上的选择又是互相独立的

    于是,我们把一个链单独拿出来考虑

    显然这个链上选择了几条边,就有几个点是不满足要求的

    那么我们可以通过一个$dp[i][j][0/1]$表示前$i$个点选了$j$条边,目前最后一条边选了没有(因为这里不能两条相邻的边同时选),来推出这条链选每一个数量的边的方案数量

    (方程参见代码)

    同时本题中有一个显然的组合容斥:

    设$ans$为答案,那么$ans=sum_{i=0}^n method(i)(-1)^i (n-i)!$,其中$method(i)$表示选$i$条边的方案数

    那么我们只要把所有的链拼起来(不同的链的连接位置强制不能选边),然后跑$dp$算答案就可以了

    感觉讲的不是很清楚......具体可以参考代码的注释

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cassert>
    #include<cmath>
    #define ll long long
    #define MOD 924844033
    using namespace std;
    inline int read(){
    	int re=0,flag=1;char ch=getchar();
    	while(ch>'9'||ch<'0'){
    		if(ch=='-') flag=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
    	return re*flag;
    }
    int n,k;
    ll dp[4010][2010][2],border[4010],f[2010],fac[2010];
    int main(){
    	n=read();k=read();int i,j,re=n%k;ll tmp=0,ans=0;
    	fac[0]=fac[1]=1;
    	for(i=2;i<=n;i++) fac[i]=fac[i-1]*i%MOD;
    	for(i=1;i<=re;i++){//这两个循环是把链拆出来,标记哪个地方是断点
    		tmp+=(n/k+1);border[tmp]=1;
    		tmp+=(n/k+1);border[tmp]=1;
    	}
    	for(i=re+1;i<=k;i++){
    		tmp+=n/k;border[tmp]=1;
    		tmp+=n/k;border[tmp]=1;
    	}
    	dp[1][0][0]=1;
    	for(i=2;i<=(n<<1);i++){
    		for(j=0;j<=n;j++){
    			dp[i][j][0]=(dp[i-1][j][0]+dp[i-1][j][1])%MOD;
    			if(!border[i-1]) dp[i][j][1]=dp[i-1][j-1][0];
                            //这里是dp方程
    		}
    	}
    	for(i=0;i<=n;i++) f[i]=(dp[n<<1][i][0]+dp[n<<1][i][1])%MOD;
    	tmp=1;
    	for(i=0;i<=n;i++){//容斥
    		ans+=tmp*f[i]*fac[n-i]%MOD;ans=(ans+MOD)%MOD;
    		tmp=-tmp;
    	}
    	printf("%lld
    ",ans);
    }
    
  • 相关阅读:
    《超级迷宫》需求规格说明
    超级迷宫冲刺个人计划安排
    审评(HelloWorld团队)
    C语言中的++与*
    a、b交换
    微服务架构浅析及实践心得
    Servlet版本冲突引起的Error
    并发编程:一个100%会发生死锁的程序
    单元测试与Mockito
    Java基础:HashMap假死锁问题的测试、分析和总结
  • 原文地址:https://www.cnblogs.com/dedicatus545/p/9476131.html
Copyright © 2011-2022 走看看