【题解】 Ironforge
Inforge
题目描述
输入描述
输出描述
输入样例
1
5 5
7 1 6 6 14
7 2 3 2
1 2
1 4
3 5
5 1
3 1
输出样例
Yes
No
Yes
Yes
Yes
题意
给定n个数a,m个询问,n个数呈链状排列,其中n-1条边每个边都有边权b,每经过一个点可以将这个点ai的所有素因子标记,一条边的b如果被标记了,则两个点认为是联通,点可以向左或向右走,现在给m次询问,每次询问是否两个点可以联通
题解
如果能求出每个点出发能够到达的最远的范围,那么就可以用O(1)回答所有询问。
首先,我们从后向前预处理出每个点只向右走能够到的最远范围,采取递归跳跃的方法,均摊复杂度O(1).
然后从前向后求每个点能够到达的最大范围即可
- 如果i可以走到i+1
- 如果i+1只向右走的范围里,有让i+1能够返回i的素因子,那么i和i+1的左右拓展区间相等
- 如果不能回去,那么L[i]=i
- 如果i走不到i+1
- 直接暴力向左向右反复横跳,复杂度都是均摊O(1)的
关于判断序列中l到r的范围内是否存在素因子的方法有很多,这里采用的是对于没一个质因子维护一个vector,来存出素因子出现的所有ai的为止,在vector上二分
复杂度: O(nlogn)
代码
#include<bits/stdc++.h>
#define re register
#define ll long long
#define inc(i,j,k) for(re int i=j;i<=k;i++)
#define dec(i,j,k) for(re int i=j;i>=k;i--)
using namespace std;
const int maxn=200010;
inline int read()
{
re int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
while(ch>='0'&&ch<='9') {x=x*10+(ch^48); ch=getchar();}
return x*f;
}
int T,n,m;
int a[maxn];
int b[maxn],l[maxn],r[maxn],low[maxn];
bool vis[maxn];
const int N=200000;
bool notprime[N+5];
int pri[N],tot;
void prim()
{
memset(notprime,0,sizeof(notprime));
notprime[0]=notprime[1]=1; tot=0;
inc(i,2,N)
{
if(!notprime[i])
{
pri[++tot]=i;
low[i]=i;
}
for(re int j=1; j<=tot && i*pri[j]<=N; j++)
{
notprime[i*pri[j]]=1;
low[i*pri[j]]=pri[j];
if(!(i%pri[j])) break;
}
}
}
inline void deal(int x)
{
inc(i,1,tot)
{
if(pri[i]>x) break;
if(x%pri[i]==0)
{
vis[pri[i]]=1;
while(x%pri[i]==0)
{
x/=pri[i];
}
}
}
}
inline void work(int x,int y)
{
memset(vis,0,sizeof(vis));
if(x<y)
{
int tmp=x;
deal(a[x]);
while(tmp>1 && vis[b[tmp-1]])
{
tmp--;
deal(a[tmp]);
}
inc(i,x,y)
{
if(i==y)
{
printf("Yes\n");
return;
}
deal(a[i]);
if(!vis[b[i]])
{
printf("No\n");
return;
}
}
}else
{
int tmp=x;
deal(a[x]);
while(tmp<n && vis[b[tmp]])
{
tmp++;
deal(a[tmp]);
}
dec(i,x,y)
{
if(i==y)
{
printf("Yes\n");
return;
}
deal(a[i]);
if(!vis[b[i-1]])
{
printf("No\n");
return;
}
}
}
}
vector <int> primpos[maxn];
//primpos数组每行矢量内元素单调递增
int clean[maxn],tim;
inline void initPrimePos()
{
inc(i,1,n)
{
int x=a[i];
while(x>1)
{
int nowprim=low[x];
while(nowprim==low[x])
{
x/=nowprim;
}
if(clean[nowprim]!=tim)
{
primpos[nowprim].clear();
clean[nowprim]=tim;
}
primpos[nowprim].push_back(i);
}
}
++tim;
}
inline bool check(int p,int l,int r)
//查询a数组从l到r是否出现了p这个质数
{
if(primpos[p].size()==0 || primpos[p].back() < l) return 0;
int tmp = *lower_bound(primpos[p].begin(),primpos[p].end(),l);
return (tmp<=r);
}
int main()
{
//freopen("1005.in","r",stdin);
//freopen("myout.out","w",stdout);
prim();
T=read();
inc(zzt,1,T)
{
n=read(); m=read();
inc(i,1,n) a[i]=read();
inc(i,1,n-1) b[i]=read();
initPrimePos();
dec(i,n,1)
{
r[i]=i;
while(r[i]<n && check(b[r[i]],i,r[i]))
{
r[i]=r[r[i]+1];
}
}
//inc(i,1,n) cout<<r[i]<<" "; cout<<endl;
inc(i,1,n)
{
if(i-1>=1 && r[i-1]>=i)
{
//如果i-1只向右跳能到达i
if(check(b[i-1],i,r[i]))
{
//如果从i开始只向右跳之后能回到i-1,那么两个情况答案相同
l[i]=l[i-1];
r[i]=r[i-1];
}else{
//尽了全力也没办法回去,所以i最左端只能到i
l[i]=i;
}
}else
{
//i-1只靠向右跳根本拓展不到i,那就先试试向左,在向右在向左无限跳
l[i]=i;
while(1)
{
bool zzt=0;
while(r[i]<n && check(b[r[i]],l[i],r[i]))
{
zzt=1;
r[i]=r[r[i]+1];
}
while(l[i]>1 && check(b[l[i]-1],l[i],r[i]))
{
zzt=1;
l[i]=l[l[i]-1];
}
if(!zzt) break;
}
}
}
inc(i,1,m)
{
re int xx,yy;
xx=read(); yy=read();
if(l[xx]<=yy && r[xx]>=yy)
{
puts("Yes");
}else puts("No");
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了