码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj 2169 连边——去重的思想

时间:2018-10-08 20:34:56      阅读:162      评论:0      收藏:0      [点我收藏+]

标签:get   多少   problem   https   i++   思想   bool   printf   阶乘   

题目: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;
}

 

bzoj 2169 连边——去重的思想

标签:get   多少   problem   https   i++   思想   bool   printf   阶乘   

原文地址:https://www.cnblogs.com/Narh/p/9756338.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!