传送门
解析:
本来可以拿
80
p
t
s
80pts
80pts暴力的
然后
u
n
o
r
d
e
r
e
d
m
a
p
−
>
m
a
p
unordered\text{ }map->map
unordered map−>map,
−
20
p
t
s
-20pts
−20pts
S
T
ST
ST表
−
>
->
−>线段树,
−
20
p
t
s
-20pts
−20pts。
结果 40 p t s 40pts 40pts全场最低滚粗。
这道题正解比暴力不知道高到那里去了,而且又短又好写。
思路:
暴力做法:
先讲一下怎么暴力做吧,不枉我考场上想得这么辛苦(大佬直接跳到后面看吧)。。。
显然我们如果固定一个起点,考虑终点分布对区间 g c d gcd gcd的影响。显然不同的 g c d gcd gcd最多只有 l o g A logA logA种,因为每次 g c d gcd gcd的变化必然是从原来的 g c d gcd gcd中减少至少一个质因子,最坏情况就是每次只减少了一个 2 2 2,所以最多只有 l o g A logA logA种不同的 g c d gcd gcd。
显然 g c d gcd gcd区间的变化是单调的,那么我们考虑二分求出这 l o g n log\text{ }n log n个区间,这里复杂度就是 l o g 2 n log^2n log2n,然后我们还需要查询区间 g c d gcd gcd, g c d gcd gcd的复杂度就是一个 O ( l o g n ) O(log\text{ }n) O(log n),然后考虑数据结构维护区间询问,用 S T ST ST表维护可以做到 O ( 1 ) O(1) O(1)提取区间。
而蒟蒻的我考场上写了线段树。。。而且暴力分还需要 u n o r d e r e d m a p unordered\text{ }map unordered map卡常。。。告辞
正解:
考虑我们必然会对每一个点求出所有 g c d gcd gcd对应的区间,那么这些区间的分界点就不能够再利用了吗?
显然是可以的,这就是这道题的关键,怎么利用原来 g c d gcd gcd的分界点?
考虑我们每次合并必然是两个相邻的区间,链表大法好。
我们考虑倒着枚举区间的左端点(当然也可以顺着枚举右端点),每次在原来的分界点中条链表,如果发现两个区间在加上新枚举的左端点后 g c d gcd gcd相等了,就直接合并这两个区间。
这样做显然是对的,因为我们是倒着枚举左端点,那么区间的 g c d gcd gcd在某个时刻一旦相等,那么再加上前面新增的节点也肯定相等。
我知道肯定还有同学一头雾水(今天写题解前至少三批同学问我这个问题),其实就是因为 g c d ( a , g ) = = g c d ( a , g ) gcd(a,g)==gcd(a,g) gcd(a,g)==gcd(a,g)当两个区间 < l , r > <l,r> <l,r>和 < l , r + 1 > <l,r+1> <l,r+1>的区间 g c d gcd gcd都为 g g g的时候,新增的节点 l − 1 l-1 l−1产生的影响就是上面这个式子,显然它们永远都在一个区间里面。
显然统计答案需要用到平衡树 m a p map map或者哈希表 u n o r d e r e d m a p unordered\text{ }map unordered map,这里明显是查找操作较多,用 H a s h Hash Hash表要优一些(我考场上怎么没想到呢。。。)
代码:
#include<bits/stdc++.h>
#include<tr1/unordered_map>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
inline int getint(){
re int num;
re char c;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
return num;
}
inline void outint(ll a){
static char ch[23];
if(a==0)pc('0');
while(a)ch[++ch[0]]=a-a/10*10,a/=10;
while(ch[0])pc(ch[ch[0]--]^48);
}
inline int gcd(int a,int b){
int tmp;
while(b){
tmp=a-a/b*b;
a=b;
b=tmp;
}
return a;
}
cs int N=500005;
int n,m;
int nxt[N],a[N],b[N];
tr1::unordered_map<int,ll>cnt;
signed main(){
n=getint();
m=getint();
for(int re i=1;i<=n;++i)a[i]=getint(),nxt[i]=i+1;
for(int re i=1;i<=m;++i)b[i]=getint(),cnt[b[i]]=0;
for(int re i=n;i;--i){
int pre=i;
for(int re j=i;j<=n;j=nxt[j]){
a[j]=gcd(a[j],a[pre]);
if(a[j]==a[pre])nxt[pre]=nxt[j];
tr1::unordered_map<int,ll>::iterator it=cnt.find(a[j]);
if(it!=cnt.end())it->second+=1ll*nxt[j]-j;
pre=j;
}
}
for(int re i=1;i<=m;++i)outint(cnt[b[i]]),pc(' ');
return 0;
}