【51Nod 1610】路径计数

Description

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

Solution

我们看到gcd=1的这个东西,一定要想想容斥可不可以(1的倍数的个数-2的倍数的个数-3的倍数的个数-5的倍数的个数……这个可以直接用莫比乌斯函数来搞)。
f[i] 表示值为i的倍数的路径数,那么答案就等于 100i=1μ(i)f[i]
我们针对一下式子,关键就是要怎么去求值i的倍数的路径书。
我们知道如果有很多个数,都是i的倍数,那么他们的gcd肯定是i的倍数。
所以对于一个i,建一个图,把所有边权是i的倍数的放入这个图中,然后求出来的答案就是i的倍数的路径数及f[i](用容斥求),最多只用开100个图。
然后每次修改的时候,求用根号的时间来重构一下这个图。
时间复杂度一共是 O(Tnm)

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
typedef long long ll;
const int maxn=5e4+7,mo=1e9+7;
int i,j,k,l,t,n,m;
int p[107],q;
int a[maxn],b[maxn],c[maxn],d[maxn];
bool bz[107];
ll ans,miu[107];
struct node{
    int b[107],tuo[107];
    ll c[107],sum,a[107][107];
    void qiu(){
        int i,j;
        fo(i,1,n)fo(j,1,n)b[j]+=a[i][j];
        d[0]=tuo[0]=0;
        fo(i,1,n)if(!b[i])d[++d[0]]=i;
        while(d[0]){
            j=d[d[0]--];
            tuo[++tuo[0]]=j;
            fo(i,1,n){
                if(a[j][i]){
                    b[i]-=a[j][i];
                    if(!b[i])d[++d[0]]=i;
                }
            }
        }
        sum=0;
        while(tuo[0]){
            i=tuo[tuo[0]--];
            c[i]=0;b[i]=0;
            fo(j,1,n){
                c[i]=c[i]+a[i][j]*c[j];
                if(c[i]>mo)c[i]%=mo;
            }
            sum=sum+c[i];
            if(sum>mo)sum-=mo;
            c[i]++;
        }
    }
}e[107];
int main(){
    freopen("fan.in","r",stdin);
    freopen("fan.out","w",stdout);
    miu[1]=1;
    fo(i,2,100){
        if(!bz[i])p[++p[0]]=i,miu[i]=-1;
        fo(j,1,p[0]){
            t=p[j]*i;
            if(t>100)break;bz[t]=1;
            if(i%p[j]==0)break;
            miu[t]=-miu[i];
        }
    }
    scanf("%d%d",&n,&m);
    fo(i,1,m)scanf("%d%d%d",&a[i],&b[i],&c[i]);
    fo(i,1,100)fo(j,1,m)if(c[j]%i==0&&miu[i]!=0)e[i].a[a[j]][b[j]]++;
    fo(i,1,100){
        if(!miu[i])continue;
        e[i].qiu();
        ans=ans+e[i].sum*miu[i];
        if(ans>mo)ans-=mo;
        if(ans%mo;
    printf("%lld\n",ans);
    scanf("%d",&q);
    while(q--){
        scanf("%d%d",&k,&t);l=c[k];
        fo(i,1,sqrt(l)){
            if(l%i==0){
                if(miu[i]!=0){
                    e[i].qiu();
                    ans=ans-e[i].sum*miu[i];
                    e[i].a[a[k]][b[k]]--;
                    e[i].qiu();
                    ans=ans+e[i].sum*miu[i];
                }
                if(i*i==l)break;
                if(miu[l/i]!=0){
                    e[l/i].qiu();
                    ans=ans-e[l/i].sum*miu[l/i];
                    e[l/i].a[a[k]][b[k]]--;
                    e[l/i].qiu();
                    ans=ans+e[l/i].sum*miu[l/i];
                }
            }
        }
        c[k]=t;
        fo(i,1,sqrt(t)){
            if(t%i==0){
                if(miu[i]!=0){
                    e[i].qiu();
                    ans=ans-e[i].sum*miu[i];
                    e[i].a[a[k]][b[k]]++;
                    e[i].qiu();
                    ans=ans+e[i].sum*miu[i];
                }
                if(i*i==t)break;
                if(miu[t/i]!=0){
                    e[t/i].qiu();
                    ans=ans-e[t/i].sum*miu[t/i];
                    e[t/i].a[a[k]][b[k]]++;
                    e[t/i].qiu();
                    ans=ans+e[t/i].sum*miu[t/i];
                }
            }
        }
        ans=(ans%mo+mo)%mo;
        printf("%lld\n",ans);
    }
}

你可能感兴趣的:(数论,容斥原理,51Nod)