ZOJ 3824 Fiber-optic Network ~莫比乌斯

若先确定父结点的取值为A
  设f(i)为子结点取值和A的gcd为i,且子结点为根的子树相邻结点都互质的方案数
  设F(i)为子结点取值和A的gcd为i的倍数,且子结点为根的子树相邻结点都互质的方案数
  则f(1) = sigma(mu[d] * F(d)) 条件 d是A的因子
想到,确定一个父结点,一个子结点,就能确定一棵子树,于是我将每条边分解成两条有向边。数组F[e][j]记录的是有向边e起点作为父结点,以终点为根结点,且根结点取值为j的倍数的方案数
每个结点对父亲取值为A的贡献是sigma(mu[d] * F(d))(d是A的因子)
每个结点的方案数就是每个取值的子结点贡献的乘积再求和

直接枚举每个点,求F[][]的值,需要剪枝,若这条有向边被访问过,说明这颗子树已经求过了。


#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
         #include 
        
          #include 
         
           #include 
          
            #include 
           
             #include 
            
              #include 
             
               #include 
              
                #include 
               
                 #include 
                
                  #include 
                  using namespace std; #define rep(i,n) for(int i=0; i 
                  
                    = N) break; use[i*pri[j]] = 1; if(i % pri[j] == 0) { mu[i*pri[j]] = 0; break; } mu[i*pri[j]] = -mu[i]; } } } void Cal(int fa,int u) { int tt = g[fa][u]; // 有向边编号 if(fa != u && pa[tt]) return; pa[tt] = 1; for(int i=L[u]; i<=R[u]; i++) cnt[u][i] = 1; repe(v,n) if(g[u][v] && v != fa) { Cal(u,v); mst(temp,0); tt = g[u][v]; repe(i,R[u]) { for(int j=i; j<=R[u]; j+=i) { temp[j] = (temp[j] + mu[i] * F[tt][i] % MOD) % MOD; } } // temp 暂时记录这个子结点对当前结点的各个值的贡献 for(int i=L[u]; i<=R[u]; i++) if(temp[i]) cnt[u][i] = cnt[u][i] * temp[i] % MOD; // cnt[u][i] 记录u结点取值i的方案数 } tt = g[fa][u]; mst(F[tt],0); for(int i=R[u]; i>=1; i--) { for(int j=(L[u] + i - 1) / i * i; j<=R[u]; j+=i) { F[tt][i] += cnt[u][j]; F[tt][i] %= MOD; } } } int main() { //freopen("out.txt","w",stdout); mobius(); int T,a,b; scanf("%d",&T); while(T--) { scanf("%d",&n); repe(i,n) scanf("%d",L+i); repe(i,n) scanf("%d",R+i); mst(g,0); rep(i,n-1) { //a = i + 1;b = i + 2; scanf("%d%d",&a,&b); g[a][b] = 2 * i + 1; g[b][a] = 2 * i + 2; } mst(pa,0); for(int i=1; i<=n; i++) { Cal(i,i); } ll cnt[N]; for(int i=1; i<=n; i++) { for(int j=L[i]; j<=R[i]; j++) cnt[j] = 1; for(int j=1; j<=n; j++) if(g[i][j]) { mst(temp,0); for(int d=1; d<=R[i]; d++) { for(int o=d; o<=R[i]; o+=d) { temp[o] = (temp[o] + mu[d] * F[g[i][j]][d] % MOD) % MOD; } } for(int d=L[i]; d<=R[i]; d++) cnt[d] = cnt[d] * temp[d] % MOD; } ll ans = 0; for(int j=L[i]; j<=R[i]; j++) ans = (ans + cnt[j] * j % MOD) % MOD; ans = (ans + MOD) % MOD; if(i > 1) printf(" "); printf("%lld",ans); }printf("\n"); } return 0; } 
                   
                 
                
               
              
             
            
           
          
         
       
      
      
     
     
    
    
   
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值