zoj 3644(关于最小公倍数的DP)

来源:互联网 发布:影响网络成瘾的因素 编辑:程序博客网 时间:2024/06/07 16:03

这个题目必须得记录一下,经典。

dp【i】【j】 i表示现在在哪个点,j表示现在是第几个约数,记忆化搜索即可,做了一些题之后,发现这是个经典的设置状态方法。

#include<stdio.h>#include<string.h>#include<math.h>#include<map>#define MOD 1000000007#define Max(a,b) ((a)>(b)?(a):(b))#define Min(a,b) ((a)<(b)?(a):(b))#define LL long longusing namespace std;const int maxn = 20005;struct edge{    int u,v,next;}e[maxn];map<LL,int> mp;int n,m,K,dp[2005][1005];int head[2005],tot,get[2005];void add_edge(int u,int v);int dfs(int now,LL x);void init();LL gcd(LL a,LL b){    return b==0?a:gcd(b,a%b);}//最大公约数LL lcm(LL a,LL b){    return a/gcd(a,b)*b;}//最小公倍数int main(){    int i;    while(scanf("%d%d%d",&n,&m,&K) != EOF)    {        memset(dp,-1,sizeof(dp));        memset(head,-1,sizeof(head));        tot = 0;        for(i = 0;i < m;i ++)        {            int u,v;            scanf("%d%d",&u,&v);            add_edge(u,v);        }        for(i = 1;i <= n;i ++) scanf("%d",&get[i]);        init();        if(mp.find(get[1]) == mp.end())        {            printf("0\n");            continue;        }        printf("%d\n",dfs(1,get[1]));    }    return 0;}void add_edge(int u,int v){    e[tot].u = u,e[tot].v = v;    e[tot].next = head[u],head[u] = tot ++;}void init(){    int i,j,cnt = 0;    mp.clear();    for(i = 1;i <= sqrt(K);i ++)    {        if(K % i) continue;        mp[i] = cnt ++;        mp[K / i] = cnt ++;    }}int dfs(int now,LL x){    if(dp[now][mp[x]] != -1) return dp[now][mp[x]];    if(now == n)    {        if(x == K)  return dp[now][mp[x]] = 1;        else return dp[now][mp[x]] = 0;    }    int i,sum = 0;    map<LL,int>:: iterator it;    for(i = head[now];i != -1;i = e[i].next)    {        int v = e[i].v;        LL y = lcm(x,get[v]);        if(y == x) continue;        it = mp.find(y);        if(it == mp.end()) continue;        sum += dfs(v,y);        sum %= MOD;    }    return dp[now][mp[x]] = sum;}