ZOJ 3644 Kitty's Game (图上DP 约数)

哎~这一场就做了三个题目,全队倒数第一,简直是太弱了。

A Kitty‘s Game (ZOJ 3644)

题目链接: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3644

题意: 给出一个有向图,每个节点有一个权值pi, 有一个人从1节点出发(其权值为1节点的权值),前往n号节点,每经过一个节点,他的权值就变成了他经过这个节点前的权值和这个节点权值的最小公倍数,如果他经过这个节点后权值不发生变化则他就不能经过这个节点,问从1节点出发到n节点且权值为k的路径有多少条。

思路: 不能重复经过一个节点表明这个图是一个有向无环图,所以可以用dp的方法来做。下面我来考虑状态,仔细思考一下就能发现,所有有效的状态都是k的约数,所以总的状态数就是k的约数个。

所以我们可以定义dp[i][j] 表示从i节点出发到n节点且权值为j的路径数。

(ps: 表示的应该是 i ~n 而不是1~i,因为如果表示的是i~n的话就得蛋疼的先对图进行拓扑排序,或者反过来来建图, 这个在图上dp中是一个很关键的点,一定要有这个概念,然后在做题是选择合适的定义。)

所以有如下的更新

dp[i][j]  += dp[v][lcm(j,pi)]  (v为i节点所指向的节点)

中间注意要判值不变的情况~~。

还要注意把所用的约数hash了。

code:

#include <cstdio>
#include <iostream>
#include <cstring>
#define clr(x,y) memset(x, y, sizeof x)
#include <cmath>
#include <vector>
#define mo 1000000007
using namespace std;

typedef long long LL;

const int maxn = 1010;
const int maxe = 20010;

int id[1010], val[1000010], ta;
int p[maxn];
int n, m, k;
LL dp[maxn][1010];

struct edge
{
    int to, next;
} G[maxe];

int head[maxn], si;

void add(int s, int t)
{
    G[si].to = t;
    G[si].next = head[s];
    head[s] = si++;
}

LL gcd(LL a, LL b)
{
    if (b == 0) return a;
    else return gcd(b, a % b);
}
LL lcm(LL a, LL b)
{
    return (a * b) / gcd(a, b);
}
LL dfs(int u, int bi)
{
    if (dp[u][bi] != -1) return dp[u][bi];
    LL & ans = dp[u][bi];
    ans = 0;
    for (int i = head[u]; i != -1; i = G[i].next)
    {
        int v = G[i].to;
        if (val[p[v]] == -1) continue;
        int mid = lcm(p[v], id[bi]);
        if (mid == id[bi]) continue;
        ans = (ans + dfs(v, val[mid])) % mo;
    }
    return ans;
}

int main()
{
    //freopen("input.txt", "r", stdin);
    int st ,ed;
    while(scanf("%d%d%d", &n, &m, &k) != EOF)
    {
        clr(val, -1); ta = 0;
        clr(head, -1); si = 0;
        clr(dp, -1);

        for (int i = 1; i <= m; i++)
        {
            scanf("%d%d", &st, &ed);
            add(st, ed);
        }
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &p[i]);
        }
        for (int i = 1; i * i <= k; i++)
        {
            if (k % i == 0)
            {
                id[ta] = i;
                val[i] = ta++;
                if (k / i != i)
                {
                    id[ta] = k / i;
                    val[k / i] = ta++;
                }
            }
        }
        dp[n][val[k]] = 1;
        if (val[p[1]] == -1) printf("0\n");
        else
        {
            printf("%lld\n", dfs(1, val[p[1]]));
        }
    }
    return 0;
}

ZOJ 3644 Kitty's Game (图上DP 约数)

时间: 2024-10-10 01:39:59

ZOJ 3644 Kitty's Game (图上DP 约数)的相关文章

ZOJ 3644 Kitty&#39;s Game(DP)

Description Kitty is a little cat. She is crazy about a game recently. There are n scenes in the game(mark from 1 to n). Each scene has a number pi. Kitty's score will become least_common_multiple(x,pi) when Kitty enter the ith scene. x is the score

ZOJ 3644 Kitty&#39;s Game dfs,记忆化搜索,map映射 难度:2

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4834 从点1出发,假设现在在i,点数为sta,则下一步的点数必然不能是sta的因数,所以不会形成环,只需从1直接走,走到n即可. 但是如果这样的话时空复杂度就都是nk,明显不满足题意,而这个时候我们可以想到,每个状态都必然是k的约数,(点数不是k的约数的节点不在路上,可以无视),而约数的个数也就k^0.5个,可以直接用map映射,这样时空复杂度都是n*k^0.5,可以解出答案

poj 3635 Full Tank? ( 图上dp )

题意: 已知每个点的加油站的油价单价(即点权),每条路的长度(边权). 有q个询问,每个询问包括起点s.终点e和油箱容量. 问从起点走到终点的最小花费.如果不可达输出impossible,否则输出最小的旅途费用. 算法: 其实要分析状态= =感觉就像是dp. 最直接的想法是  每到一个点都加上要走到下一个点所需要的油量.但是走的路不同,到底怎么处理加多少的问题呢? 因此想到分解状态,即拆点.每到一个点都+1单位的油量,然后把这个状态加入队列.另外如果现在油箱内的油足够达到下一点, 则更新状态,把

zoj1232Adventure of Super Mario(图上dp)

题目连接: 啊哈哈,点我点我 思路: 这个题目是一个图上dp问题,先floyd预处理出图上所有点的最短路,但是在floyd的时候,把能够用神器的地方预处理出来,也就是转折点地方不能为城堡..预处理完毕后,就是一个dp问题了...dp[][],两维分别表示到达的地点和使用神器的次数..这样这个问题就得到了解决.. 题目: Adventure of Super Mario Time Limit: 2 Seconds      Memory Limit: 65536 KB After rescuing

poj 3249 Test for Job 图上dp(记忆化搜索)

题意: 给一个n个点的DAG,每个点有一个值p,现在要在图上找一个入度为0到出度为0的路径,使路径上的点的p值和最大. 分析: dp[v]记录以点v为起点能获得的最大值,搜一遍即可. 代码: //poj 3249 //sep9 #include <iostream> using namespace std; const int maxN=100024; const int maxM=1000024; int n,m,e; int p[maxN],head[maxN],dp[maxN],vis[

Codeforces Round #455 (Div. 2) E. Coprocessor DAG图上dp

E. Coprocessor 题意:n 个任务,每个任务在 主 / 副 处理器上执行.每个任务可能依赖于其它的一些任务,副处理器每次可以处理多个任务.但如果一个任务要在副处理器上执行,那它所依赖的任务要么已执行完了,要么和它一起在这个副处理器上同时执行.问副处理器最少调用多少次. 直白一点讲,就是给出一个 DAG 图,n 个点, m 条边,每个点的权值为 0 或1 .操作:直接相互连通的权值为 1 的点可以一次处理掉. 问最少操作多少次. tags:因为是DAG 图,直接跑 dp dp[i] 表

Codeforces 918D MADMAX 图上dp 组合游戏

题目链接 题意 给定一个 \(DAG\),每个边的权值为一个字母.两人初始各占据一个顶点(可以重合),轮流移动(沿着一条边从一个顶点移动到另一个顶点),要求每次边上的权值 \(\geq\) 上一次的权值.无法移动者输. 要求:对所有可能的初始情况,给出一张胜负表. 思路 特殊情况 两人在同一个顶点上,那么必然是先手输: 如果有\(u\rightarrow v\)边,并且先手在 \(u\) 上,后手在 \(v\) 上,且先手此时可以移动(判断边的权值),那么必然是先手赢 一般情况 考虑用 \(dp

HDU 3249 Test for job (有向无环图上的最长路,DP)

 解题思路: 求有向无环图上的最长路,简单的动态规划 #include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> #include <algorithm> #include <vector> #include <cmath> #define LL long long using namespace std; const int

hdu 5001 概率DP 图上的DP

http://acm.hdu.edu.cn/showproblem.php?pid=5001 当时一看是图上的就跪了 不敢写,也没退出来DP方程 感觉区域赛的题  一则有一个点难以想到 二则就是编码有点难度. 这个题: 我一直的思路就是1-能到达i的概率 就是不能到达i的概率,然后三维方程巴拉巴拉,,,,把自己搞迷糊 正确做法: dp[k][j]   经过j步到达k点 并且不经过i点的概率 这么设的原因是,就可以求不能到达i点的概率了.   不能到达i点的概率就是segma(dp[v][j-1]