bzoj 2169 连边——去重的思想

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2169

如果之前都去好重了,可以看作这次连的边只会和上一次连的边重复。

可以认为从上上次的状态到这次的状态,转移的过程对于上上次的每个状态来说都是把剩余位置所有连边的可能性遍历了恰好一遍!即,当前连了 i 条边,与上次连的边重复的数量就是 C(n,2)-(i-2)(n个点里选2个是一共有多少空位放边,上上次已经放了 i-2 条,这次与上次可以重复的位置有该式那么多个)。

关于同种方案因为连边顺序不同导致的重复计数,只要每次算好一条边之后除以 i 即可;意即对每一种方案,这次放的边可以在已经放的 i 条边中的任意一条,导致重复。不这样去重而最后除以所填边数的阶乘是不行的,因为自己的转移在由有重复的状态转移来的时候不成立。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1005,mod=10007;
int n,m,t,dp[N][N];
bool deg[N];
int rdn()
{
  int ret=0;bool fx=1;char ch=getchar();
  while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
  while(ch>='0'&&ch<='9') ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar();
  return fx?ret:-ret;
}
int calc(int a){return (a*(a-1)>>1)%mod;}
int pw(int x,int k)
{
  int ret=1;while(k){if(k&1)ret=ret*x%mod;x=x*x%mod;k>>=1;}return ret;
}
int main()
{
  n=rdn(); m=rdn(); t=rdn();
  for(int i=1,u,v;i<=m;i++)
    {
      u=rdn(); v=rdn();
      deg[u]=!deg[u]; deg[v]=!deg[v];
    }
  int cnt=0;
  for(int i=1;i<=n;i++) cnt+=deg[i];
  dp[0][cnt]=1;
  for(int i=1;i<=t;i++)
    {
      int d=pw(i,mod-2);
      for(int j=0;j<=n;j++)
    {
      dp[i][j]=dp[i-1][j]*j%mod*(n-j)%mod;
      dp[i][j]=(dp[i][j]+dp[i-1][j+2]*calc(j+2))%mod;
      if(j>=2)
          dp[i][j]=(dp[i][j]+dp[i-1][j-2]*calc(n-j+2))%mod;
      if(i>1)dp[i][j]-=dp[i-2][j]*(calc(n)-(i-2))%mod;
      if(dp[i][j]<0)dp[i][j]+=mod;
      dp[i][j]=dp[i][j]*d%mod;
    }
    }
  printf("%d\n",dp[t][0]%mod);
  return 0;
}

 

转载于:https://www.cnblogs.com/Narh/p/9756338.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值