题意定义f(l,r)为去掉[l,r]部分后剩下的数任意两个数的最大公约数的最大值

现在求f(l,r)的和

由于每个数ai最大只有200000,因此我们穷举因子x,记录以其为因子的a[i]的i值并按i升序。

下面我们从大到小穷举每个因子x,我们依次计算以f(l,r)=x的区间数,

有了上述的维护信息,我们很容易三种情况的区间满足f(l,r)=x(我就不具体细说了)

令pre[i]表示当前以第i个位置结尾的区间满足任意j(1<=j<=pre[i]),f(j,i)<x

显然f(l,r)=x的区间数=当前还没计算过的区间数-∑pre[i]

这就要维护∑pre[i],每次修改是将一段区间pre[i]>x的数赋值为x

显然pre[i]是单调不上升,因此可以用线段树维护之

 1 #include<bits/stdc++.h>
 2 
 3 using namespace std;
 4 typedef long long ll;
 5 int laz[200010*4],mi[200010*4],mx[200010*4];
 6 ll tr[200010*4];
 7 vector<int> g[200010];
 8 int n,m;
 9 void build(int i,int l,int r)
10 {
11     laz[i]=-1;
12     if (l==r) mx[i]=mi[i]=tr[i]=l;
13     else {
14         int m=(l+r)>>1;
15         build(i*2,l,m);
16         build(i*2+1,m+1,r);
17         tr[i]=tr[i*2]+tr[i*2+1];
18         mx[i]=max(mx[i*2],mx[i*2+1]);
19         mi[i]=min(mi[i*2],mi[i*2+1]);
20     }
21 }
22 
23 void work(int i,int l,int r,int x,int y,int z)
24 {
25    // if (i==1) cout <<z<<endl;
26     if (x>y) return;
27     if (mx[i]<=z) return;
28     if (x<=l&&y>=r&&mi[i]>z)
29     {
30         mi[i]=mx[i]=laz[i]=z;
31         tr[i]=(ll)(r-l+1)*z;
32     }
33     else {
34         int m=(l+r)>>1;
35         if (laz[i]>-1)
36         {
37             laz[i*2]=laz[i*2+1]=laz[i];
38             mi[i*2]=mx[i*2]=laz[i];
39             mi[i*2+1]=mx[i*2+1]=laz[i];
40             tr[i*2]=(ll)laz[i]*(m-l+1);
41             tr[i*2+1]=(ll)laz[i]*(r-m);
42             laz[i]=-1;
43         }
44         if (x<=m) work(i*2,l,m,x,y,z);
45         if (y>m) work(i*2+1,m+1,r,x,y,z);
46         tr[i]=tr[i*2]+tr[i*2+1];
47         mx[i]=max(mx[i*2],mx[i*2+1]);
48         mi[i]=min(mi[i*2],mi[i*2+1]);
49     }
50 }
51 
52 int main()
53 {
54     scanf("%d",&n);
55     for (int i=1; i<=n; i++)
56     {
57         int x;scanf("%d",&x);
58         for (int j=1; j*j<=x; j++)
59             if (x%j==0)
60             {
61                 g[x/j].push_back(i);
62                 if (j*j!=x) g[j].push_back(i);
63             }
64         m=max(m,x);
65     }
66     build(1,1,n);
67     ll pre=(ll)n*(n+1)/2;
68     ll ans=0;
69     for (int i=m; i; i--)
70     {
71         if (g[i].size()<2) continue;
72         int j=g[i].size()-1;
73         work(1,1,n,g[i][0]+1,g[i][j]-1,g[i][0]);
74         work(1,1,n,1,g[i][j-1]-1,0);
75         work(1,1,n,g[i][1]+1,n,g[i][1]);
76         ans+=(pre-tr[1])*(ll)i;
77        // cout <<tr[1]<<endl;
78         pre=tr[1];
79     }
80     printf("%lld\n",ans);
81 }
View Code

 

posted on 2017-01-28 23:32  acphile  阅读(308)  评论(0编辑  收藏  举报