题解讲的非常清楚,写个题解加深一下自己的印象
对原题抽象、建模后,对于每个op=1op=1的操作其实就是若干个一次函数y=A_i*(h_j-dis_i)y=Ai∗(hj−disi),求满足y=t_jy=tj的i的数量。dis_i=abs(i-x)disi=abs(i−x)
等式变完形后就是dis_i=h_j-t_j/A_idisi=hj−tj/Ai,显然对于该op=1op=1的操作,会爆炸的村庄需要满足以下两个条件:
1.A_j|t_jAj∣tj
2.dis_i=h_j-t_j/A_idisi=hj−tj/Ai
所以大致思路就是枚举t_jtj的因子yy,确定dis_idisi,然后判断距离为dis_idisi的村庄A_iAi是否等于yy
接着处理几个细节,用调和级数预处理出ii的倍数,建立一颗数xx指向所有因子的森林,在枚举t_jtj因子时可以ln(n)ln(n)。
t_j==0tj==0时需要特判,因为没有存0的因子
dis_idisi相同的村庄只有两个,桶排一下。
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+7,maxm=1e6*25,mod=1e9+7;
typedef long long int ll;
typedef unsigned long long ull;
int head[maxn],Next[maxm],to[maxm],tot,n,m,x,a[maxn][2];
inline void add(int x,int y) {
to[++tot]=y;
Next[tot]=head[x];
head[x]=tot;
}
inline void preint() {
for(int i=1; i<=1000000; ++i)
for(int j=i; j<=1000000; j+=i)
add(j,i);
for(int i=1,y,j; i<=n; ++i) {
cin>>y;
if(a[j=abs(i-x)][0]) a[j][1]=y;
else a[j][0]=y;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n>>m>>x;
preint();
int op,h,t,ans(0);
while(m--) {
cin>>op>>h;
if(op&1) {
cin>>t;
if(!t) {
if(!h) {
ans+=1;
continue;
}
if(x+h<=n) ans+=1;
if(x-h>=1) ans+=1;
continue;
}
for(int i=head[t],y=to[i],dis; i; i=Next[i],y=to[i]) {
dis=h-t/y;
if(dis<0) continue;
if(a[dis][0]==y) ans+=1;
if(a[dis][1]==y) ans+=1;
}
} else {
cout<<ans<<'\n';
cout.flush();
}
}
return 0;
}