CF671C Ultimate Weirdness of an Array
链接:https://www.luogu.com.cn/problem/CF671C
题目描述:定义\(f(i,j)\)表示一个序列去掉\([i,j]\)这一段后任意两数\(gcd\)的最大值,求所有\(f(i,j)\)的和。
题解:这个东西不能莫比乌斯反演,因为有一个最大值,那么我们考虑暴力维护\(gcd=d\)的\((i,j)\)对数。
考虑\(gcd\)可能为\(d\)的数对满足什么条件,它必须是\([sa,sb],[ta,n],[1,tb]\)的一个子序列(\(sa\)为\(gcd\)可能为\(d\)的最小值,\(ta\)为\(gcd\)可能为\(d\)的次小值,\(sb\)为\(gcd\)可能为\(d\)的大值,\(tb\)为\(gcd\)可能为\(d\)的次大值)
由于\(gcd(i,j)\)可能为\(d\)即\(d|gcd(i,j)\),那么\(gcd(i,j)\)被最后一次覆盖的就是它的权值,那么我们可以倒序枚举\(d\),再对区间进行覆盖,这样只要将不被覆盖的覆盖,然后统计要覆盖的区间对数即可。
将区间拖到网格图上,令区间\([l,r]\)对应从下到上的第\(l\)行,从左到右的第\(r\)列的元素,这个网格图即为一个下三角。因为下面的覆盖,上面的就一定会被覆盖,所以我们可以令\(d_{i}\)表示网格第\(i\)列的高度,一开始\(d_{i}=i\)。
那么区间覆盖就变成了对\(d\)区间取\(min\),区间求和。
由于也是用与网格图一样的下三角进行覆盖,那么\(d_{i}<=j(j<i)\)一定是\(i\)被以\(j\)左边为左端点的下三角将它覆盖了,此时\(d_{j}\)也被覆盖了,\(d_{j}<=d_{i}\)。
那么由于\(d\)单调,我们可以二分出覆盖区间,然后区间\(min\)变为区间赋值,这样线段树就可以直接维护了。
#include<iostream>
#include<cstdio>
#include<set>
#define N 200000
using namespace std;
struct node
{
long long l,r,data,lazy;
};
node tree[800001];
int read()
{
char c=0;
int sum=0;
while (c<'0'||c>'9')
c=getchar();
while ('0'<=c&&c<='9')
{
sum=sum*10+c-'0';
c=getchar();
}
return sum;
}
int used[200001];
long long n,a[200001],min2[200001],max2[200001],ans,min1[200001],max1[200001];
void spread(int k)
{
if (tree[k].lazy==-1)
return;
tree[k*2].data=tree[k].lazy*(tree[k*2].r-tree[k*2].l+1);
tree[k*2].lazy=tree[k].lazy;
tree[k*2+1].data=tree[k].lazy*(tree[k*2+1].r-tree[k*2+1].l+1);
tree[k*2+1].lazy=tree[k].lazy;
tree[k].lazy=-1;
return;
}
void push_up(int k)
{
tree[k].data=tree[k*2].data+tree[k*2+1].data;
}
void build(int k,int l,int r)
{
tree[k].l=l;
tree[k].r=r;
tree[k].lazy=-1;
if (l==r)
{
tree[k].data=l;
return;
}
int mid=(l+r)/2;
build(k*2,l,mid);
build(k*2+1,mid+1,r);
push_up(k);
return;
}
void change(int k,int l,int r,int d,int ad)
{
if (tree[k].l==l&&tree[k].r==r)
{
ans-=(d*(tree[k].r-tree[k].l+1)-tree[k].data)*ad;
tree[k].lazy=d;
tree[k].data=d*(tree[k].r-tree[k].l+1);
return;
}
spread(k);
int mid=(tree[k].l+tree[k].r)/2;
if (l<=mid&&r<=mid)
{
change(k*2,l,r,d,ad);
push_up(k);
return;
}
if (l>=mid+1&&r>=mid+1)
{
change(k*2+1,l,r,d,ad);
push_up(k);
return;
}
change(k*2,l,mid,d,ad);
change(k*2+1,mid+1,r,d,ad);
push_up(k);
return;
}
int query(int k,int x)
{
if (tree[k].l==tree[k].r)
return tree[k].data;
spread(k);
int mid=(tree[k].l+tree[k].r)/2;
if (x<=mid)
return query(k*2,x);
else
return query(k*2+1,x);
}
void ADD(int l,int r,int ad)
{
if (l>r)
return;
int first=1,last=n,res=n+1,mid;
while (first<=last)
{
mid=(first+last)/2;
if (query(1,mid)>=l)
{
res=min(res,mid);
last=mid-1;
}
else
first=mid+1;
}
if (res<=r)
change(1,res,r,l-1,ad);
return;
}
int main()
{
n=read();
for (int i=1;i<=n;++i)
{
a[i]=read();
used[a[i]]=i;
}
for (int i=1;i<=N;++i)
min1[i]=1e9,max1[i]=-1e9,min2[i]=1e9,max2[i]=-1e9;
for (int i=1;i<=N;++i)
for (int j=i;j<=N;j+=i)
if (used[j])
{
if (used[j]<min1[i])
{
min2[i]=min1[i];
min1[i]=used[j];
}
else if (used[j]<min2[i])
min2[i]=used[j];
if (used[j]>max1[i])
{
max2[i]=max1[i];
max1[i]=used[j];
}
else if (used[j]>max2[i])
max2[i]=used[j];
}
build(1,1,n);
for (int i=N;i>=1;--i)
{
ADD(min2[i]+1,n,i);
ADD(1,max2[i]-1,i);
ADD(min1[i]+1,max1[i]-1,i);
}
printf("%lld\n",ans);
return 0;
}
本文来自博客园,作者:zhouhuanyi,转载请注明原文链接:https://www.cnblogs.com/zhouhuanyi/p/16983734.html

浙公网安备 33010602011771号