• Pair of Numbers


    题意:

    给一长度为n的正整数序列,对于一个$[l,r]$,如果存在$d ∈ [l,r]$ 满足 $a(d)|a(i) (i∈[l, r])$ 则称之为合法子串。

    找出所有的最长合法子串。

    解法:

    1. $O(n sum_{i=1}^n { au ( a(i) ) })$ 做法,枚举a序列中出现的数字d,将 i*d 所在的位置标上1,

    然后从所有d所在的位置左右暴力拓展。

    注意到只有下一位为1时才会拓展,所以暴力拓展的复杂度和标1的复杂度相同。

    考虑每个数字a(i),最多被访问了它的约数次,所以总复杂度 $O(n sum_{i=1}^n { au ( a(i) ) })$

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <vector>
     5 #include <algorithm>
     6 
     7 #define N 300010
     8 #define M 1000010
     9 
    10 using namespace std;
    11 
    12 int n,m,tot,ans,a[N],a0[N];
    13 bool v[N],del[N];
    14 vector<int> pos[M],ansv;
    15 
    16 int main()
    17 {
    18 //    freopen("test.txt","r",stdin);
    19     while(~scanf("%d",&n))
    20     {
    21         for(int i=1;i<=n;i++) del[i]=0;
    22         ansv.clear();
    23         ans=m=1;
    24         for(int i=1;i<=n;i++)
    25         {
    26             scanf("%d",&a[i]);
    27             m = max(m, a[i]);
    28             pos[a[i]].push_back(i);
    29             a0[i]=a[i];
    30         }
    31         sort(a0+1,a0+n+1);
    32         tot=1;
    33         for(int i=2;i<=n;i++)
    34             if(a0[i]!=a0[i-1]) a0[++tot]=a0[i];
    35         for(int d=1;d<=tot;d++)
    36         {
    37             int x=a0[d];
    38         //    cout <<"tet "<< x << endl;
    39             for(int i=x;i<=m;i+=x)
    40             {
    41                 int nl=pos[i].size();
    42                 for(int j=0;j<nl;j++) v[pos[i][j]]=1;
    43             }
    44             int len=pos[x].size();
    45         //    for(int i=1;i<=n;i++) cout<<v[i];
    46         //    cout<<endl;
    47             for(int i=0;i<len;i++)
    48             {
    49                 int tmp=pos[x][i];
    50                 if(del[tmp]) continue;
    51                 del[tmp]=1;
    52                 int l=tmp,r=tmp;
    53                 while(l>1 && v[l-1])
    54                 {
    55                     l--;
    56                     if(a[l] == x) del[l]=1;
    57                 }
    58                 while(r<n && v[r+1])
    59                 {
    60                     r++;
    61                     if(a[r] == x) del[r]=1;
    62                 }
    63                 if(r-l+1>ans)
    64                 {
    65                     ans=r-l+1;
    66                     ansv.clear();
    67                     ansv.push_back(l);
    68                 }
    69                 else if(r-l+1==ans)
    70                     ansv.push_back(l);
    71             }
    72             for(int i=x;i<=m;i+=x)
    73             {
    74                 int nl=pos[i].size();
    75                 for(int j=0;j<nl;j++) v[pos[i][j]]=0;
    76             }
    77         }
    78         for(int i=1;i<=m;i++) pos[i].clear();
    79         int len=ansv.size();
    80         cout << len << ' ' << ans-1 << endl;
    81         sort(ansv.begin(), ansv.end());
    82         for(int i=0;i<len;i++) cout << ansv[i] << ' ';
    83         cout << endl;
    84     }
    85     return 0;
    86 }
    View Code

    2.考虑优化上一个做法,将a(i)按照一定的顺序枚举,用类似对a(i)做筛法的方式,由a(i)推到i*a(i),这样均摊复杂度大概是

    $O(nloglogn)$

    3.仔细观察,从小到大枚举a(i),对于每一个a(i)暴力向左向右拓展,并记录相应位置是否被拓展到过,

    对于当前枚举到的值为 a(i) 的位置

      (1)如果其向左 / 向右拓展到了标记过的位置,那一定无法再次拓展。

      (2)如果相应位置已经被访问过,那么从当前位置拓展,答案一定会变差。

    这样,我们只要枚举a(i),暴力拓展即可。

    每个位置只会被访问一次 且 每个数字被访问一次,总复杂度 $O(m)$ (懒得加桶排了)

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <vector>
     5 #include <algorithm>
     6 
     7 #define N 300010
     8 #define M 1000010
     9 
    10 using namespace std;
    11 
    12 int n,m,tot,ans,a[N],a0[N];
    13 bool del[N];
    14 vector<int> pos[M],ansv;
    15 
    16 int main()
    17 {
    18 //    freopen("test.txt","r",stdin);
    19     while(~scanf("%d",&n))
    20     {
    21         for(int i=1;i<=n;i++) del[i]=0;
    22         ansv.clear();
    23         ans=m=1;
    24         for(int i=1;i<=n;i++)
    25         {
    26             scanf("%d",&a[i]);
    27             m = max(m, a[i]);
    28             pos[a[i]].push_back(i);
    29             a0[i]=a[i];
    30         }
    31         sort(a0+1,a0+n+1);
    32         tot=1;
    33         for(int i=2;i<=n;i++)
    34             if(a0[i]!=a0[i-1]) a0[++tot]=a0[i];
    35         for(int d=1;d<=tot;d++)
    36         {
    37             int x=a0[d];
    38             int len=pos[x].size();
    39             for(int i=0;i<len;i++)
    40             {
    41                 int tmp=pos[x][i];
    42                 if(del[tmp]) continue;
    43                 del[tmp]=1;
    44                 int l=tmp,r=tmp;
    45                 while(l>1 && a[l-1]%x==0) del[--l]=1;
    46                 while(r<n && a[r+1]%x==0) del[++r]=1;
    47                 if(r-l+1>ans)
    48                 {
    49                     ans=r-l+1;
    50                     ansv.clear();
    51                     ansv.push_back(l);
    52                 }
    53                 else if(r-l+1==ans)
    54                     ansv.push_back(l);
    55             }
    56         }
    57         for(int i=1;i<=m;i++) pos[i].clear();
    58         int len=ansv.size();
    59         cout << len << ' ' << ans-1 << endl;
    60         sort(ansv.begin(), ansv.end());
    61         for(int i=0;i<len;i++) cout << ansv[i] << ' ';
    62         cout << endl;
    63     }
    64     return 0;
    65 }
    View Code
  • 相关阅读:
    理解Javascript_13_执行模型详解
    vs2005的快捷键
    去除 VS.Net 2003 项目的 VSS 息的脚本
    VS2005的隐藏快捷键
    程序员,你离坐牢还有多远
    对路径XXX的访问被拒绝(文件操作权限)的解决方法
    安装VS2005 SP1之后无法更改或卸载VS2005的处理方法
    强大的.NET反编译工具Reflector及插件
    反编译工具Reflector下载(集成两个常用.net插件,FileGenerator和FileDisassembler)
    两种彻底删除VIEWSTATE的方法
  • 原文地址:https://www.cnblogs.com/lawyer/p/6484578.html
Copyright © 2020-2023  润新知