• The 15th UESTC Programming Contest Preliminary G


    地址:http://acm.uestc.edu.cn/#/problem/show/1564

    题目:

    G - GC?(X,Y)

    Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 131071/131071KB (Java/Others)

    One positive integer can be represented by the product of some prime numbers.

    Sort the prime numbers, such like 60=223560=2∗2∗3∗5, 180=22335180=2∗2∗3∗3∗5.

    The GCPGCP(Greatest Common Prefix) of two positive integers is defined as the longest prefix of the multiplication of sorted prime numbers.

    For example, GCP(60,180)=Longest_Prefix(2235,22335)=223=12GCP(60,180)=Longest_Prefix(2∗2∗3∗5,2∗2∗3∗3∗5)=2∗2∗3=12.

    Now, for a given array AiAi, calculate

    1i<jNGCP(Ai,Aj)∑1≤i<j≤NGCP(Ai,Aj)
    .

     

    Input

    The first line contains a number NN.

    The second line contains NN integers AiAi.

    1N105,1Ai1071≤N≤105,1≤Ai≤107

    Output

    Output the sum described above.

    Sample input and output

    Sample InputSample Output
    5
    1 2 8 5 10
    13

    Hint

    In the sample,

    GCP(1,2)=GCP(1,8)=GCP(1,5)=GCP(1,10)=GCP(2,5)=GCP(8,5)=GCP(5,10)=1GCP(1,2)=GCP(1,8)=GCP(1,5)=GCP(1,10)=GCP(2,5)=GCP(8,5)=GCP(5,10)=1,

    GCP(2,8)=GCP(2,10)=GCP(8,10)=2GCP(2,8)=GCP(2,10)=GCP(8,10)=2.

     题解:
      第一步肯定是先把所有数分解质因数。时间复杂度O(1e7+n*sqrt(n))
      后面求最大公共前缀的做法有三种:
      1.把所有质因数乘积表达式插入trie树中,然后O(n)遍历所有节点计算答案即可。
        分析:1e7内的树最多分解成10个不同的质数乘积,25个可重复质数的乘积。
        1e5个数分解成质因数后,所含的质数不会太多,应该在1e5~2e6之间(具体多少我也不清楚,队友算过,但我忘了)
        可以对出现的质因数哈希一下,然后插入trie树中,每个节点最少记录两个值end,sum。
        end:到该节点结尾的数的数量。
        sum:经过该节点的数的数量(包括在该节点结尾的数)
        由上可知trie树上的节点不会超过25n,一般情况远远小于这个数。
        trie要选用动态分配内存的方法或者左儿子右兄弟的建树方法(也就是改成二叉树),不然mle。
        不过我用动态分配内存的trie树时还是mle了,改了半天还没卡过去,其他队倒是好多人卡过去了(不过他们可能用的左儿子右兄弟的trie树)
        左儿子右兄弟的trie树:
            节省空间复杂度,除去了用节点,但是增加了时间复杂度。但只是增加时间复杂度的常数。(对于这题还是十分适用的)
        遍历trie树即可计算答案,可用前缀和优化。
        时间复杂度O(25n),空间复杂度看trie树建法
      2.把所有质因数乘积表达式当做字符串从小到大排序,然后对每个表达式二分最远匹配点,并计算答案。
        排序函数cmp:根据两质因数乘积表达式第i个质数的大小,和质数个数排序。
        二分:
          对于第i个数,通过二分求出所有前缀所能匹配到的最远位置。
          然后对于倒序计算这些前缀的贡献。
        为什么要倒序,请看下例:
          2*2,2*2*2,2*5  
          计算第一个数2*2的匹配位置数列是2,1.(从0开始计数)
          此时计算第二个2所能匹配的位置的贡献(前缀)2*2  * (1-0)(最远匹配长度)
          计算第一个2所能匹配的位置的贡献(前缀)2*2  * (2-1)(最远长度减上一个质数的匹配长度)

        时间复杂度:O(nlogn+25nlogn)

      3.哈希前缀

      PS:比赛时一开始想二分算法,结果因为听到其他人用trie数过了,然后就一直怼trie然后。。。gg!
      要是坚定自己的想法就好了,可惜了。。。
      实现难度t:rie树远大于二分
      (如果讲错请各位指教)
      二分ac代码:
     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 #define PB push_back
     4 typedef long long LL;
     5 const int K=1e5+7;
     6 const int maxn=1e7+7;
     7 vector<int>num[K],va;
     8 vector<LL>vb;
     9 int n,pri[maxn],v[K],tol,tag[maxn];
    10 LL ans;
    11 bool cmp(const vector<int> &ta,const vector<int> &tb)
    12 {
    13     for(int i=0,j=min(ta.size(),tb.size());i<j;i++)
    14     if(ta[i]!=tb[i])    return ta[i]<tb[i];
    15     return ta.size()<tb.size();
    16 }
    17 void init(void)
    18 {
    19     for (int i = 2; i * i < maxn; i++)
    20     {
    21         if (tag[i]) continue;
    22         for (int j = i; j * j < maxn; j++)
    23             tag[i*j] = 1;
    24     }
    25     for (int i = 2; i < maxn; i++)
    26         if (!tag[i])
    27             pri[tol++] = i;
    28     for(int i=0; i<n; i++)
    29     {
    30         int t=v[i];
    31         num[i].PB(1);
    32         for(int j=0; pri[j]*pri[j]<=t; j++)
    33         {
    34             if(v[i]%pri[j]) continue;
    35             while(v[i]%pri[j]==0)   v[i]/=pri[j],num[i].PB(pri[j]);
    36         }
    37         if(v[i]>1)  num[i].PB(v[i]);
    38     }
    39 }
    40 void bs(int x)
    41 {
    42     int sum=1;
    43     int l,r,mid,tmp=n-1;
    44     va.clear(),vb.clear();
    45     for(int i=0;i<num[x].size();i++)
    46     {
    47         l=x,r=tmp;
    48         while(l<=r)
    49         {
    50             mid=(l+r)/2;
    51             if(num[mid].size()>i&&num[mid][i]==num[x][i])
    52                 tmp=mid,l=mid+1;
    53             else
    54                 r=mid-1;
    55         }
    56         sum*=num[x][i];
    57         va.PB(tmp),vb.PB(sum);
    58     }
    59     for(int i=num[x].size()-1,ls=x;i>=0;i--)
    60         ans+=vb[i]*(va[i]-ls),ls=va[i];
    61 }
    62 int main(void)
    63 {
    64     scanf("%d",&n);
    65     for(int i=0;i<n;i++)
    66         scanf("%d",v+i);
    67     init();
    68     sort(num,num+n,cmp);
    69     for(int i=0;i<n;i++)
    70         bs(i);
    71     cout<<ans<<endl;
    72     return 0;
    73 }

      动态分配内存的trie树,未ac,mle16了,不想改成左儿子右兄弟了:

      1 #include <bits/stdc++.h>
      2 
      3 using namespace std;
      4 
      5 #define MP make_pair
      6 #define PB push_back
      7 #define MAXNUM 27500
      8 typedef long long LL;
      9 typedef pair<int,int> PII;
     10 const double eps=1e-8;
     11 const double pi=acos(-1.0);
     12 const int K=1e5+7;
     13 const int mod=1e9+7;
     14 const int maxn=1e7+7;
     15 
     16 
     17 vector<int>num[K];
     18 map<int,int>hs;
     19 int n,pri[maxn],v[K],tol,tag[maxn];
     20 int ths[K];
     21 LL ans;
     22 void make_prime()
     23 {
     24     for (int i = 2; i * i < maxn; i++)
     25     {
     26         if (tag[i])
     27             continue;
     28         for (int j = i; j * j < maxn; j++)
     29             tag[i*j] = 1;
     30     }
     31     for (int i = 2; i < maxn; i++)
     32         if (!tag[i])
     33             pri[tol++] = i;
     34 }
     35 void init()
     36 {
     37     for(int i=0; i<n; i++)
     38     {
     39         int t=v[i];
     40         num[i].PB(1);
     41         for(int j=0; pri[j]*pri[j]<=t; j++)
     42         {
     43             if(v[i]%pri[j]) continue;
     44             while(v[i]%pri[j]==0)   v[i]/=pri[j],num[i].PB(pri[j]);
     45         }
     46         if(v[i]>1)  num[i].push_back(v[i]);
     47     }
     48 }
     49 typedef struct Trie
     50 {
     51     int sum,ed;
     52     Trie *next[MAXNUM];
     53 }Trie;
     54 Trie *root;
     55 void TrieInit(int sz)
     56 {
     57     root = (Trie *)malloc(sizeof(Trie));
     58     root->sum=root->ed=0;
     59     for(int i=0;i<sz;i++)
     60         root->next[i]=NULL;
     61     for(int j=0;j<n;j++)
     62     {
     63         Trie *tem=root;
     64         for(int i=0;i<num[j].size();i++)
     65         {
     66             //printf("x=%d:%d %d
    ",j,num[j][i],hs[num[j][i]]);
     67             if(tem->next[hs[num[j][i]]]==NULL)
     68             {
     69                 Trie *cur = (Trie *)malloc(sizeof(Trie));
     70                 for(int k=0;k<sz;k++)
     71                     cur->next[k]=NULL;
     72                 cur->sum=cur->ed=0;
     73                 tem->next[hs[num[j][i]]]=cur;
     74             }
     75             tem = tem->next[hs[num[j][i]]];
     76             tem->ed++;
     77         }
     78         tem->sum++;
     79     }
     80 }
     81 void dfs(Trie *x,LL y,int sz)
     82 {
     83     Trie *tem = x,*cur;
     84     LL ta=0,tb=0,ff=0;
     85     if(tem->ed-tem->sum>0)
     86     for(int i=0;i<sz;i++)
     87     if(tem->next[i]!=NULL)
     88     {
     89         cur=tem->next[i],ff++;
     90         tb+=ta*cur->ed;
     91         ta+=cur->ed;
     92         if(cur->ed)dfs(cur,y*ths[i],sz);
     93     }
     94     if(ff>1||tem->sum>0)
     95         ans+=y*tb+y*tem->sum*(tem->ed-tem->sum)+y*tem->sum*(tem->sum-1)/2LL;
     96 
     97 }
     98 
     99 int main(void)
    100 {
    101     int cnt=0;
    102     scanf("%d",&n);
    103     make_prime();
    104     for(int i=0;i<n;i++)
    105         scanf("%d",v+i);
    106     init();
    107     for(int i=0;i<n;i++)
    108     {
    109         //printf("x=%d: ",i);
    110         for(int j=0;j<num[i].size();j++,cnt++)
    111             //printf("%d ",num[i][j]),
    112             ths[cnt]=num[i][j];
    113         //printf("
    ");
    114     }
    115     sort(ths,ths+cnt);
    116     int sz=unique(ths,ths+cnt)-ths;
    117     for(int i=0;i<sz;i++)
    118         hs[ths[i]]=i;
    119     TrieInit(sz);
    120     for(int i=0;i<sz;i++)
    121     if(root->next[i]!=NULL)
    122         dfs(root->next[i],ths[i],sz);
    123     cout<<ans<<endl;
    124     return 0;
    125 }

     

     
        
  • 相关阅读:
    前端面试题六
    前端面试题五
    前端面试题四
    前端面试题之三
    前端面试题分享二
    前端面试题分享一
    JS学习笔记一
    git使用学习笔记一
    常见User-Agent
    ado.net之SQLServer和Oracle (sys_cursor) 数据库链接——获取结果集方式对比
  • 原文地址:https://www.cnblogs.com/weeping/p/6629430.html
Copyright © 2020-2023  润新知