51Nod 算法马拉松17 路径计数 莫比乌斯函数加暴力构图

题目大意

现在定义路径上所有边权的最大公约数定义为一条路径的值。
现在给定一个 N 个点M条边有向无环图。进行 T 次修改操作,每次修改一条边的边权,每次修改后输出有向无环图上路径的值为1的路径数量(对109取模)。

N100
M50000
T500
100

解题思路

我们可以用莫比乌斯来做这题,设 Fi 为边权为 i 的倍数的路径的数量,最后答案就是100i=1Fimiui。那么考虑现在怎么得到 F 数组。

我们知道一个数的约数最多就只有根号个,而且这题的边权最大只有100,那么考虑建100幅图,在第i幅图统计 Fi 的答案。而对于一条边权为 Aj 连接 u,v 的边,假如 Aj % k=0 那么就在第 k 幅图的u,v连一条边。对于 Fi 就是第 i 幅图的路径条数。而修改只有500次,复杂度是OTmaxAiN。足以通过本题。

程序

//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

const int MAXN = 105, MAXM = 5e4 + 5;
const int Mo = 1e9 + 7;

struct Node {
    int u, v, w;
} Q[MAXM];

bool Flag[MAXN];
int N, M, Mu[MAXN], Fac[MAXN];
int tot[MAXN], Ans[MAXN];

struct Solve {
    int Map[MAXN][MAXN], Num[MAXN], Ord[MAXN], F[MAXN], Ans;

    void Get() {
        for (int i = 1; i <= N; i ++)
            for (int j = 1; j <= N; j ++) 
                Num[j] += Map[i][j];
        static int D[MAXN];
        int top = 0, p = 0;
        for (int i = 1; i <= N; i ++)
            if (!Num[i]) D[++ top] = i;
        while (top) {
            int Now = D[top --];
            Ord[++ p] = Now;
            for (int i = 1; i <= N; i ++) 
                if (Map[Now][i]) {
                    Num[i] -= Map[Now][i];
                    if (!Num[i]) D[++ top] = i;
                } 
        }
        Ans = 0;
        for (; p; p --) {
            int Now = Ord[p];
            F[Now] = 1;
            for (int i = 1; i <= N; i ++) 
                F[Now] = (F[Now] + Map[Now][i] * 1ll * F[i] % Mo) % Mo;
            Ans = (Ans + F[Now] - 1) % Mo;
        }
    }
} P[MAXN];

int Prep() {
    Mu[1] = 1;
    for (int i = 2; i < MAXN; i ++) {
        if (!Flag[i]) {
            Mu[i] = -1;
            Fac[++ Fac[0]] = i;
        }
        for (int j = 1; j <= Fac[0]; j ++) {
            if (1ll * i * Fac[j] >= MAXN) break;
            Flag[i * Fac[j]] = 1;
            if (i % Fac[j]) Mu[i * Fac[j]] = -Mu[i]; else {
                Mu[i * Fac[j]] = 0;
                break;
            }
        }
    }
}

int main() {
    scanf("%d%d", &N, &M);
    Prep();
    for (int i = 1; i <= M; i ++) 
        scanf("%d%d%d", &Q[i].u, &Q[i].v, &Q[i].w);
    for (int i = 1; i <= M; i ++) 
        for (int j = 1; j <= Q[i].w; j ++) 
            if (Q[i].w % j == 0 && Mu[j] != 0) P[j].Map[Q[i].u][Q[i].v] ++;
    int Ans = 0;
    for (int i = 1; i < MAXN; i ++) 
        if (Mu[i] != 0) {
            P[i].Get();
            Ans = ((Ans + P[i].Ans * 1ll * Mu[i]) % Mo + Mo) % Mo;
        }
    printf("%d\n", Ans);
    int C;
    scanf("%d\n", &C);
    for (int i = 1; i <= C; i ++) {
        int Ord, Val;
        scanf("%d%d", &Ord, &Val);
        memset(Flag, 0, sizeof Flag);
        for (int j = 1; j <= Q[Ord].w; j ++) 
            if (Q[Ord].w % j == 0 && Mu[j]) {
                P[j].Map[Q[Ord].u][Q[Ord].v] --;
                Flag[j] = 1;
            }
        Q[Ord].w = Val;
        for (int j = 1; j <= Q[Ord].w; j ++) 
            if (Q[Ord].w % j == 0 && Mu[j]) {
                P[j].Map[Q[Ord].u][Q[Ord].v] ++;
                Flag[j] = 1;
            }

        for (int j = 1; j < MAXN; j ++) 
            if (Mu[j] && Flag[j]) P[j].Get();
        Ans = 0;
        for (int j= 1; j < MAXN; j ++) 
            if (Mu[j]) Ans = ((Ans + P[j].Ans * Mu[j]) % Mo + Mo) % Mo; 
        printf("%d\n", Ans);
    }
}
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看READme.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值