zoukankan      html  css  js  c++  java
  • UOJ33 [UR #2] 树上GCD 【点分治】【容斥原理】【分块】

    题目分析:

    树上点对问题首先想到点分治。假设我们进行了点分治并递归地解决了子问题。现在我们合并问题。

    我们需要找到所有经过当前重心$ c $的子树路径。第一种情况是LCA为当前重心$ c $。考虑以$ 1 $为根的$ c $的子树。那么首先在子问题中先斥掉不经过$ c $的路径。然后对于$ c $的子树处理出距离数组。用桶存储。

    从大到小枚举最大公因数$ d $,求出所有距离为$ d $倍数的点的个数。然后做乘法得到$ num1 $。再考虑$ num1 $中GCD不等于$ d $的数有哪些。实际上我们早就计算出了GCD为$ d $的倍数的情况了,只需要把这一部分斥掉就行了。所以处理$ c $的子树的点对的时间复杂度是$ O(nlogn) $。

    再考虑$ c $的祖先的儿子到$ c $的子树中的点的情况。不难想到类似的处理方法。开个桶存储距离。由于点分治的特性。我们很容易就可以证明这样所有桶的最大值之和是不会超过$ O(n) $的。这样枚举的时间复杂度是有保障的。对于$ c $的某个祖先的子树中的点,枚举GCD为$ d $。 那么我们要在$ c $的子树中找到所有的距$ c $祖先$ d $的倍数的点。 由于我们拔高了LCA,这时候我们不能简单地枚举倍数。重新审视问题,发现其实它是求的c中从i开始每隔j个的和。采用分块算法,对于小于等于$ sqrt{n} $的情况共有$ sqrt{n} $个不同的起点。每个不同的间隔都会完全覆盖依次整个桶。共覆盖了$ sqrt{n} $次。所以预处理的时间复杂度为$ O(n*sqrt{n}) $。对于大于$ sqrt{n} $的情况可以暴力计算。 

    我们每处理一个重心的时间复杂度为$ O(n*log n+n*sqrt{n}) $分为两半,前半部分总时间复杂度为$ O(n*log n*log n) $,后半部分带入主定理知为$ O(n*sqrt{n}) $

    代码:

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 
      4 const int maxn = 200005;
      5 
      6 int n;
      7 int dep[maxn],f[maxn];
      8 long long ans[maxn];
      9 vector <int> g[maxn];
     10 
     11 int arr[maxn],sz[maxn],dg[maxn],presum[maxn];
     12 
     13 void read(){
     14     scanf("%d",&n);
     15     for(int i=2;i<=n;i++) {
     16     scanf("%d",&f[i]);
     17     g[f[i]].push_back(i);
     18     g[i].push_back(f[i]);
     19     }
     20 }
     21 
     22 void DFS(int now,int dp) {
     23     dep[now] = dp;
     24     for(int i=0;i<g[now].size();i++){
     25     if(g[now][i] == f[now]) continue;
     26     DFS(g[now][i],dp+1);
     27     }
     28 }
     29 
     30 void dfs1(int now,int fa){
     31     sz[now] = 0;dg[now] = 0;
     32     for(int i=0;i<g[now].size();i++){
     33     if(g[now][i] == fa) continue;
     34     if(arr[g[now][i]]) continue;
     35     dfs1(g[now][i],now);
     36     sz[now] += sz[g[now][i]];
     37     dg[now] = max(dg[now],sz[g[now][i]]);
     38     }
     39     sz[now]++;
     40 }
     41 
     42 int dfs2(int now,int fa,int tot){
     43     int res = now;dg[now] = max(tot-sz[now],dg[now]);
     44     for(int i=0;i<g[now].size();i++){
     45     if(g[now][i] == fa) continue;
     46     if(arr[g[now][i]]) continue;
     47     int z = dfs2(g[now][i],now,tot);
     48     if(dg[res] > dg[z])res = z;
     49     }
     50     return res;
     51 }
     52 
     53 int depest,h[maxn],sum[maxn];
     54 long long nowans[maxn];
     55 int Final[500][500];
     56 
     57 int oth,arv[maxn],s2[maxn];
     58 
     59 void dfs3(int now,int len,int stop){
     60     h[len]++;depest = max(depest,len);
     61     for(int i=0;i<g[now].size();i++){
     62     if(g[now][i] == f[now]) continue;
     63     if(arr[g[now][i]]!=0 && arr[g[now][i]]<=stop) continue;
     64     dfs3(g[now][i],len+1,stop);
     65     }
     66 }
     67 
     68 void dfs4(int now,int cant,int stop,int lca){
     69     arv[dep[now]-dep[lca]]++; oth = max(oth,dep[now]-dep[lca]);
     70     for(int i=0;i<g[now].size();i++){
     71     if(g[now][i] == f[now] || g[now][i] == cant) continue;
     72     if(arr[g[now][i]]!=0 && arr[g[now][i]]<=stop) continue;
     73     dfs4(g[now][i],cant,stop,lca);
     74     }
     75 }
     76 
     77 void solve_dec(int now,int last,int ht){
     78     depest = 0;
     79     dfs3(now,1,ht);
     80     for(int i=depest;i>=1;i--){
     81     for(int j=1;i*j<=depest;j++) sum[i] += h[i*j];
     82     nowans[i] = 1ll*sum[i]*(sum[i]-1)/2;
     83     for(int j=2;i*j<=depest;j++) nowans[i]-=nowans[i*j];
     84     ans[i] -= nowans[i];
     85     }
     86     for(int i=0;i<=depest;i++) h[i] = 0,sum[i] = 0,nowans[i] = 0;
     87 }
     88 
     89 void solve_add(int now,int ht){
     90     depest = 0;
     91     dfs3(now,0,ht);
     92     for(int i=depest;i>=1;i--){
     93     for(int j=1;i*j<=depest;j++) sum[i] += h[i*j]; //multi d
     94     nowans[i] = 1ll*sum[i]*(sum[i]-1)/2;
     95     for(int j=2;i*j<=depest;j++) nowans[i]-=nowans[i*j];
     96     ans[i] += nowans[i];
     97     }
     98     for(int i=1;i<=depest;i++) nowans[i] = 0;
     99     
    100     for(int i=1;i*i<=depest;i++) for(int j=0;j<i;j++)
    101         for(int k=j;k<=depest;k+=i) Final[i][j] += h[k];
    102     // in subtree
    103     
    104     int tp = f[now],last = now,rem = 0;
    105     while(tp&&(arr[tp]>ht||arr[tp] == 0)){
    106     oth = 0;rem++;
    107     dfs4(tp,last,ht,tp); // tp mean lca
    108     for(int i=oth;i>=1;i--){
    109         for(int j=1;i*j<=oth;j++) s2[i] += arv[i*j];
    110         if(i*i<=depest) 
    111         nowans[i] = 1ll*s2[i]*Final[i][(((-rem)%i)+i)%i];
    112         else{
    113         int frst = (((-rem)%i)+i)%i;
    114         int tot = 0;
    115         for(int k =frst;k<=depest;k+=i) tot += h[k];
    116         nowans[i] = 1ll*s2[i]*tot;
    117         }
    118         for(int j=2;i*j<=oth;j++) nowans[i] -= nowans[i*j];
    119         ans[i] += nowans[i];
    120     }
    121     last = tp; tp = f[tp];
    122     for(int i=0;i<=oth;i++) arv[i] = s2[i] = nowans[i] = 0;
    123     }
    124     //out subtree
    125     for(int i=0;i<=depest;i++) h[i] = 0,sum[i] = 0;
    126     for(int i=1;i*i<=depest;i++) for(int j=0;j<i;j++) Final[i][j] = 0;
    127 }
    128 
    129 void divide(int now,int last,int ht){
    130     dfs1(now,0); int pw = sz[now];
    131     int pt = dfs2(now,0,pw);
    132     arr[pt] = ht;
    133     for(int i=0;i<g[pt].size();i++){
    134     if(arr[g[pt][i]]) continue;
    135     divide(g[pt][i],pt,ht+1);
    136     }
    137     if(last&&f[now]==last) solve_dec(now,last,ht-1);
    138     solve_add(pt,ht);
    139 }
    140 
    141 void work(){
    142     DFS(1,1);
    143     divide(1,0,1);
    144     for(int i=1;i<=n;i++) presum[dep[i]-1]++;
    145     for(int i=n;i>=1;i--) presum[i] += presum[i+1];
    146     for(int i=1;i<=n;i++) ans[i] += presum[i];
    147     for(int i=1;i<n;i++) printf("%lld
    ",ans[i]);
    148 }
    149 
    150 
    151 int main(){
    152     read();
    153     work();
    154     return 0;
    155 }
  • 相关阅读:
    告知服务器意图的HTTP方法
    初识HTTP协议
    与HTTP关系密切的三个协议:IP,TCP,DNS
    TCP/IP的分层管理
    CSS position 详解
    ASP.NET MVC的 ActionResult跳转视图方法
    html js打印
    C# 跳出循环几种方法详解
    c# 下载文件封装方法
    c# MVC @Styles.Render @Scripts.Render 使用方法
  • 原文地址:https://www.cnblogs.com/Menhera/p/9082906.html
Copyright © 2011-2022 走看看