51 nod 1610 路径计数(Moblus+dp)

1610 路径计数

基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题
 
路径上所有边权的最大公约数定义为一条路径的值。
给定一个有向无环图。
T次修改操作,每次修改一条边的边权,每次修改后输出有向无环图上路径的值为1的路径数量(对1,000,000,007取模)。
Input
第一行两个整数n和m,分别表示有向无环图上的点数和边数。(1<=n<=100,1<=m<=50,000)
第2~m+1行每行三个数x,y,z,表示有一条从x到y权值为z的边。(1<=x,y<=n,1<=z<=100)
第m+2行一个数T,表示修改操作次数(1<=T<=500)。
接下来T行每行两个数x,y,表示修改第x条边(按照读入的顺序)的边权为y(1<=x<=m,1<=y<=100)。
Output
T+1行,修改前和每次修改操作后输出答案。
Input示例
4 4
1 2 2
2 4 3
1 3 4
3 4 2
4
1 5
2 10
3 3
4 6
Output示例
1
1
0
1
0

 

/*
51 nod 1610 路径计数(Moblus+dp)

problem:
路径上所有边权的最大公约数定义为一条路径的值。给定一个有向无环图。
T次修改操作,每次修改一条边的边权,每次修改后输出有向无环图上路径的值为1的路径数量(对1,000,000,007取模)。

solve:
感觉直接在图上求GCD的话很麻烦,而且还涉及到修改.
后来发现可以考虑通过容斥来求GCD,这样的话就转换成了图上面长度为i的路径的个数.
开始时记录路径长度w[i]以及它的约数. (w[i] = 4的话, 可以看成有 1,2,4三条边)

于是通过枚举gcd便能够在 100*n*n内求出来所有路径值的情况.
在修改的时候,可以发现只会 影响被移除的数和添加的数以及它们的约数. 处理一下
然后通过moblus实现容斥就能求出gcd = 1的情况.

hhh-2016/09/09-20:59:44
*/
#pragma comment(linker,"/STACK:124000000,124000000")
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <stdio.h>
#include <cstring>
#include <vector>
#include <math.h>
#include <queue>
#include <set>
#include <map>
#define lson  i<<1
#define rson  i<<1|1
#define ll long long
#define clr(a,b) memset(a,b,sizeof(a))
#define scanfi(a) scanf("%d",&a)
#define scanfs(a) scanf("%s",a)
#define scanfl(a) scanf("%I64d",&a)
#define scanfd(a) scanf("%lf",&a)
#define key_val ch[ch[root][1]][0]
#define eps 1e-7
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
const ll mod = 1000000007;
const int maxn = 135;
const double PI = acos(-1.0);
const int limit = 33;


template<class T> void read(T&num)
{
    char CH;
    bool F=false;
    for(CH=getchar(); CH<'0'||CH>'9'; F= CH=='-',CH=getchar());
    for(num=0; CH>='0'&&CH<='9'; num=num*10+CH-'0',CH=getchar());
    F && (num=-num);
}
int stk[70], tp;
template<class T> inline void print(T p)
{
    if(!p)
    {
        puts("0");
        return;
    }
    while(p) stk[++ tp] = p%10, p/=10;
    while(tp) putchar(stk[tp--] + '0');
    putchar('\n');
}
int n,m,q,id;
ll dp[maxn],num[maxn];
ll ma[maxn][maxn][maxn];

int tot;
int is_prime[maxn];
ll mu[maxn];
int prime[maxn];

void Moblus()
{
    tot = 0;
    mu[1] = 1;
    memset(is_prime,0,sizeof(is_prime));
    for(int i = 2; i < maxn-10; i++)
    {
        if(!is_prime[i])
        {
            prime[tot++] = i;
            mu[i] = -1;
        }

        for(int j = 0; j < tot && i*prime[j] < maxn-10; j++)
        {
            is_prime[i*prime[j]] = 1;
            if(i % prime[j])
            {
                mu[i*prime[j]] = -mu[i];
            }
            else
            {
                mu[i*prime[j]] = 0;
                break;
            }
        }
    }
}

ll dfs(int u,int gcd)
{
    if(dp[u] != -1) return dp[u];
    ll ans = 0;
    for(int i = 1; i <= n; i++)
    {
        if(ma[gcd][u][i])
        {
            ans = (ll)(ans +  ma[gcd][u][i] + (ll)ma[gcd][u][i]*dfs(i,gcd)%mod)%mod;
        }
    }
    return dp[u] = ans;
}

ll solve(int gcd)
{
    clr(dp,-1);
    ll ans = 0;
    for(int i = 1; i <= n; i++)
    {
        if(dp[i] == -1)
            dfs(i,gcd);
    }
    for(int i = 1; i <= n; i++)
        ans = (ans + dp[i])%mod;
    return ans;
}

void debug()
{
    for(int i = 1; i <= 10; i++)
    {
        num[i] = solve(i);
        cout << num[i] <<" ";
    }
    cout << endl;
}

int u[maxn*maxn*5],vec[maxn*maxn*5];
int v[maxn*maxn*5];
int x[maxn*maxn*5];
int main()
{
//    freopen("in.txt","r",stdin);
    int y;
    Moblus();
    read(n),read(m);
    memset(ma,0,sizeof(ma));
    for(int i = 1; i <= m; i++)
    {
        read(u[i]),read(v[i]);
        read(x[i]);
        for(int j = 1; j * j <= x[i]; j++)
        {
            if(x[i] % j) continue;
            ma[j][u[i]][v[i]] ++;
            if(j * j != x[i])
                ma[x[i]/j][u[i]][v[i]] ++;
        }
    }
    for(int i = 1; i <= 100; i++)
    {
        num[i] = solve(i);
    }
//    debug();
    read(q);
    ll ans = 0;
    for(int i = 1; i <= 100; i++)
    {
        ans = (ans + (ll)mu[i] * num[i] +mod)%mod;
    }
     printf("%I64d\n",ans);
    int id,cnt;
    for(int i = 1; i <= q; i++)
    {
        ans = 0,cnt = 0;
        read(id),read(y);
        int a = u[id],b = v[id];
        for(int i = 1; i*i <= x[id]; i++)
        {
            if(x[id] % i) continue;
            ma[i][a][b] --,vec[cnt++] = i;
            if(i * i != x[id])
                ma[x[id]/i][a][b] --,vec[cnt++] = x[id]/i;
        }
        x[id] = y;
        for(int i = 1; i*i <= x[id]; i++)
        {
            if(x[id] % i) continue;
            ma[i][a][b] ++,vec[cnt++] = i;
            if(i*i != x[id])
                ma[x[id]/i][a][b] ++,vec[cnt++] = x[id]/i;
        }

        for(int i = 0; i < cnt; i++)
        {
            num[vec[i]] = solve(vec[i]);
        }
//        debug();
        for(int i = 1; i <= 100; i++)
        {
            ans = (ans + (ll)mu[i] * num[i]+mod)%mod;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

  

转载于:https://www.cnblogs.com/Przz/p/5858052.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值