T1 Reverse
https://www.gxyzoj.com/d/hzoj/p/P980
假設1在點i時,這個1可以透過一次翻轉到達那些點,將這些點和i連邊,此時答案就是s到x的最短路
但是,此時邊數也會到達\(n^2\)級別
考慮最佳化,因為邊權均為1,所以可以直接bfs,可以發現每個點能轉移的點的奇偶性是有限制的,而且每個點至多被更新一次
所以可以將所有點按奇偶性分類後丟進set,然後找到轉移邊界,暴力更新即可
注意,選出的子串的長度必須為k,所以在靠近兩個端點的地方是不能取點作為中心的
程式碼:
#include<cstdio>
#include<set>
#include<queue>
#define it set<int>::iterator
using namespace std;
int n,m,s,k,b[100005],ans[100005];
double lmid,rmid;
set<int> s1,s2;
queue<int> q;
void bfs(int st)
{
for(int i=1;i<=n;i++)
{
ans[i]=1e9;
}
ans[st]=0;
q.push(st);
if(st%2) s1.erase(st);
else s2.erase(st);
while(!q.empty())
{
int u=q.front();
q.pop();
double ls=u-lmid,rs=rmid-u;
int l=lmid-ls,r=rmid+rs;
l=max(l,max(0,u-k+1)),r=min(r,min(n,u+k-1));
// printf("%d %d %d %d\n",u,l,r,ans[u]);
if((u+k+1)%2)
{
it lid=s1.lower_bound(l);
it rid=s1.lower_bound(r);
while(*lid<=r&&lid!=s1.end())
{
if(b[*lid])
{
lid++;
continue;
}
ans[*lid]=ans[u]+1;
q.push(*lid);
lid++;
}
// printf("1");
lid=s1.lower_bound(l);
if(*rid<=r&&rid!=s1.end()) rid++;
s1.erase(lid,rid);
}
else
{
it lid=s2.lower_bound(l);
it rid=s2.lower_bound(r);
while(*lid<=r&&lid!=s2.end())
{
if(b[*lid])
{
lid++;
continue;
}
ans[*lid]=ans[u]+1;
q.push(*lid);
lid++;
}
lid=s2.lower_bound(l);
if(*rid<=r&&rid!=s2.end()) rid++;
s2.erase(lid,rid);
}
}
}
int main()
{
freopen("reverse.in","r",stdin);
freopen("reverse.out","w",stdout);
scanf("%d%d%d%d",&n,&k,&m,&s);
lmid=(1+k)*1.0/2.0,rmid=(n+n-k+1)*1.0/2.0;
for(int i=1;i<=m;i++)
{
int x;
scanf("%d",&x);
b[x]=1;
}
for(int i=1;i<=n;i++)
{
if(i%2) s1.insert(i);
else s2.insert(i);
}
bfs(s);
for(int i=1;i<=n;i++)
{
if(ans[i]!=1e9) printf("%d ",ans[i]);
else printf("-1 ");
}
return 0;
}