hdu5378
关于逆元的相关知识已经总结到这里:求i模p的逆元中
还有关于这道题,在测试样例时,一直是错误的,最后才发现是求快速幂即ni函数时,其中的x没有设为long long int,谨记,虽然x乘完自己后或模上1000000007,但是还没模之前可能超过int的范围,但不会超过long long int的范围,所以要把x设为long long int。
不过关于这道题,嗯,用概率来求方法数,谁也限制不住人类的大脑啊!!!(今天学校停水,早晨没吃到牛肉馅的包子,中午懒在机房,所以吃了一天的面包,咸菜,不说了,说多了都是泪!!!我要吃肉!!!)
2015.8.29:
今天再次看着道题,一开始偷瞄到一点是通过概率求总共有多少种方法,但是死活也记不起到底该怎么求,依稀的记忆+推理得出是二维的dp,然后就死活就认准了dp[i][j]代表以i为根节点的子树中有j个minister,然后就只能朝着背包的方向去想了,最后感觉这样能做,但是不如原先的方法简单,虽然记不起原先的方法是什么了。然后打开题解,结果发现竟然看不懂了,在网上找题解,从新整理这道题,最后总算发现,不是智商问题,还算欣慰。
题解中dp[i][j]代表1到i中有几个点在以它为根节点的子树中是最大的,那么相当于是前i个点中选择了j个让它成为以它为根节点的子树中最大的,并没有其它的含义了,不要再多想了,如果可以的话,想要冲着刚才的自己这样喊,然后递归就行了。
下面证明为什么在1到i中有j个是以它为根节点的子树中最大的,那么就只有j个minister:
首先,可以得知,这j个点必定是minister;
其次,假如说存在第j+1个minister,那么存在某个包括这个点的子树中这个点最大,任何一棵子树如果包括这个点的话,那么就肯定包括以这个点为根节点的那棵子树,所以在以这个点为根节点的子树中这个点也是最大的,与前面的假设,相违背,所以不可能存在第j+1个minister。
最后,没有最后。
#include<stdio.h> #include<string.h> #include<iostream> using namespace std; #define N 1010 #define MOD 1000000007 long long int dp[N][N]; int siz[N]; int head[N],to[2*N],nextedge[2*N]; int cou; long long int A[N]; void add(int a,int b){ to[cou]=b;nextedge[cou]=head[a];head[a]=cou++; } void dfs(int u,int fa){ siz[u]=1; for(int i=head[u];i!=-1;i=nextedge[i]){ int v=to[i]; if(v==fa){ continue; } else{ dfs(v,u); siz[u]+=siz[v]; } } return; } long long int ni(long long int x){ int y=MOD-2 ; long long int ans=1; while(y){ if(y%2){ ans=ans*x%MOD; } y=y/2; x=x*x%MOD; } return ans; } int main(){ int t; int n; int k; int a,b; A[0]=1; for(int i=1;i<N;i++){ A[i]=A[i-1]*i%MOD; } scanf("%d",&t); for(int cas=1;cas<=t;cas++){ scanf("%d%d",&n,&k); memset(head,-1,sizeof(head)); cou=0; for(int i=1;i<n;i++){ scanf("%d%d",&a,&b); add(a,b); add(b,a); } dfs(1,-1); memset(dp,0,sizeof(dp)); dp[1][1]=ni(siz[1]); dp[1][0]=(siz[1]-1)*dp[1][1]%MOD; for(int i=2;i<=n;i++){ long long int tempg=ni(siz[i]); dp[i][0]=dp[i-1][0]*(siz[i]-1)%MOD*tempg%MOD; for(int j=1;j<=i;j++){ dp[i][j]=(dp[i-1][j]*(siz[i]-1)%MOD*tempg%MOD+dp[i-1][j-1]*tempg%MOD)%MOD; } } printf("Case #%d: %d\n",cas,(A[n]*dp[n][k]%MOD)); } return 0; }