思路很巧妙,首先,很明显,数轴上关于原点对称的一个点对,不论移动了多少次,都任然是对称的。
再看眼数据范围,发现其实点分布的比较紧,考虑直接将所有点看做一个整体(数轴上一个线段),然后依次移动。
关键的是,若这个整体横跨了原点的话,那么在原点的点就有答案了,对于剩下的部分,设在正半轴、负半轴的部分长度为 \(len1,len2\),若 \(len1>len2\),则将负半轴的部分对称到正半轴,他之后的位置相当于对称后的位置的相反数。
反之同理。
而这个不断对称的过程,我们可以用并查集解决,每个节点就表示初始所在的位置。由于要判断相反数,所以用带权并查集即可。
由于有负半轴,所以记得要把数轴上的位置转化到并查集上,细节较多。
#include<bits/stdc++.h>
#define int long long
#define m_p make_pair
#define p_b push_back
#define num first
#define color second
using namespace std;
inline int rd(){
int x=0,f=1; char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if (ch=='-') f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
return x*f;
}
const int N=3e5+5,M=1e6;
int a[N],D[N],n,m;
int fa[M*2+5],val[M*2+5];//0:一样,1:变化
int find(int x){
if(fa[x]==x) return x;
int dad=fa[x];
fa[x]=find(fa[x]);
val[x]^=val[dad];
return fa[x];
}
int ans[M*2+5];
signed main(){
n=rd(),m=rd();
for(int i=1;i<=n;i++) a[i]=rd();
for(int i=1;i<=m;i++) D[i]=rd();
int l=a[1],r=a[n];//在数轴上的范围,并查集的范围
for(int i=0;i<=(M<<1);i++) fa[i]=i;
int x,fx;
for(int i=1;i<=m;i++){
if(l>0) l-=D[i],r-=D[i];
else l+=D[i],r+=D[i];
if(l<=0&&0<=r){//横跨了远点
x=M;fx=find(x);
ans[fx]=i;
if(-l<r){//移动往正半轴
for(int i=1;i<=-l;i++){
fa[M-i]=M+i;
val[M-i]^=1;
}
l=1;
}
else{
for(int i=1;i<=r;i++){
fa[M+i]=M-i;
val[M+i]^=1;
}
r=-1;
}
}
}
int now;
for(int i=1;i<=n;i++){
x=find(a[i]+M);
if(ans[x]) printf("Yes %lld\n",ans[x]);
else{
now=x-M;
if(val[a[i]+M])now*=-1;
printf("No %lld\n",now);
}
}
return 0;
}
标签:ch,数轴,半轴,题解,查集,ARC149D,对称,define
From: https://www.cnblogs.com/123456xwd/p/18194878