0%

[ARC149D] Simultaneous Sugoroku

一道很不错的思维题,没啥算法基础。

场上离做出来就差最后一步。

题目链接

题意

在一个坐标轴上有 个棋子,第 个棋子的坐标是

现在将每一个棋子移动 次,移动规则是这样的:

对于第 次移动,如果某个棋子坐标大于 ,将它向左移 ;如果坐标小于 ,将它向右移 ;坐标为 则不变。

对于每一个棋子,按照规则移动完这 次以后,输出以下值:

如果这个棋子的坐标最后不是 ,输出坐标,否则输出这个棋子在第几次移动后到达坐标

题解

首先我们观察范围,会发现 ,值域非常小。

这提示我们不妨假设 范围内,每一个地方都有一个棋子。如果我们能把这些答案都求出来,那么题目中所给的 个坐标肯定也没问题。

然后我们会发现一个性质。

假如有两个棋子在第 次操作后坐标互为相反数/相等。

那么它们在第 次操作之后,坐标肯定也互为相反数/相等。

这个性质很显然。

然后我们再来想,对于一段棋子 ,如果我们进行一次操作,里面的棋子最多有三个结果:落在原点,或者原点左边/右边。

落在原点的棋子我们不用看,以后无论怎么操作,它肯定都在原点。

现在假如有一部分棋子落在原点左边,有一部分棋子落在原点右边。

结合一些上面的性质。

我们会发现有一些棋子会”结合“起来,它们的坐标以后都互为相反数,这个时候我们只需要计算它们其中的一部分。

举一个例子:

现在 上各有一个棋子,我们现在进行操作

所有棋子向左移 格,坐标为 的棋子到了原点,棋子 到了原点左边,棋子 到了原点右边。

不难发现,棋子 现在坐标为 ,棋子 坐标为 ,它们的坐标现在互为相反数,所以以后它们的坐标都会互为相反数。我们只要知道了棋子 之后某一时刻的坐标,就能知道棋子 在那个时候的坐标。

同理,棋子 ,棋子 ,棋子 都有这样的关系。

所以在之后的某一个时刻中,我们如果知道了棋子 的坐标,我们就能知道棋子 的坐标。

现在回到原来的问题:坐标 上各有一个棋子,经过若干次操作,求它们的最终位置。

这告诉我们,每一次操作之后,如果整个序列被分成了两段,那么我们只需要计算出长的那一段的最终位置,并且记录短的那一段与长的那一段,坐标的关系即可。

假如我们称每一次操作后,被分成的两段中,较长的一段为”关键段“。

那么我们只需要在每一次操作后维护,当前关键段的位置,以及关键段由哪些棋子组成。然后记录”关键段“与较短段的关系。

上面的这两个东西都可以直接递推。

记录关系可以直接进行建边。我们将”关键段“里的棋子连一条边向较短段里的对应棋子,表示后者的坐标是前者的相反数。

例如,在上面那个例子中,我们就可以从 ……

处理到最后一个询问时,我们就知道关键段中点的答案(以及其中一些算出的,到达原点的棋子编号),这个时候我们就可以通过之前连的有向边,求出 中所有点的答案。

可以看一看官方题解中的图,假设记忆。

代码

细节还是比较多的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include<algorithm>
#include<assert.h>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<map>
using namespace std;
using ll=long long;
using pli=pair<ll,int>;
using pi=pair<int,int>;
template<typename A>
using vc=vector<A>;
inline int read()
{
int s=0,w=1;char ch;
while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
vc<int>t[1000001];
bool t0[1000001];
int ans[1000001];
int x[1000001];
int d[1000001];
vc<int>a0;
int l,r;
int n,m;
int sum;
void dfs(int num)
{
for(int p:t[num])
{
t0[p]=t0[num],ans[p]=ans[num];
if(!t0[p]) ans[p]=-ans[p];
dfs(p);
}
}
int main()
{
n=read(),m=read(),l=1,r=1000000;
for(int i=1;i<=n;i++) x[i]=read();
for(int i=1;i<=m;i++) d[i]=read();
for(int i=1;i<=m;i++)
{
if(l>r) break;
if(l>0)
{
if(l>d[i]||r<d[i])
{
l-=d[i],r-=d[i],sum-=d[i];
continue;
}
t0[d[i]-sum]=1,ans[d[i]-sum]=i;
a0.push_back(d[i]-sum);
int l1=l,r1=d[i]-1;
int l2=d[i]+1,r2=r;
if(r1-l1>=r2-l2)
{
for(int i=0;i<=r2-l2;i++) t[r1-i-sum].push_back(l2+i-sum);
l=l1-d[i],r=r1-d[i],sum-=d[i];
}
else
{
for(int i=0;i<=r1-l1;i++) t[l2+i-sum].push_back(r1-i-sum);
l=l2-d[i],r=r2-d[i],sum-=d[i];
}
}
else
{
if(-r>d[i]||-l<d[i])
{
l+=d[i],r+=d[i],sum+=d[i];
continue;
}
t0[-d[i]-sum]=1,ans[-d[i]-sum]=i;
a0.push_back(-d[i]-sum);
int l1=l,r1=-d[i]-1;
int l2=-d[i]+1,r2=r;
if(r1-l1>=r2-l2)
{
for(int i=0;i<=r2-l2;i++) t[r1-i-sum].push_back(l2+i-sum);
l=l1+d[i],r=r1+d[i],sum+=d[i];
}
else
{
for(int i=0;i<=r1-l1;i++) t[l2+i-sum].push_back(r1-i-sum);
l=l2+d[i],r=r2+d[i],sum+=d[i];
}
}
}
for(int i=l;i<=r;i++) ans[i-sum]=i,dfs(i-sum);
for(int i:a0) dfs(i);
for(int i=1;i<=n;i++)
{
if(t0[x[i]]) printf("Yes %d\n",ans[x[i]]);
else printf("No %d\n",ans[x[i]]);
}
return 0;
}

感谢观看!