ABC219_F Cleaning Robot 同余
题意
初始有在((0,0))位置有机器在二维平面,给定长度为(N)的上下左右合成的命令,命令重复(K)次,求经过的不同点个数
[1 leq K leq 10^{12}\
1 leq N leq 2 imes 10^5
]
分析
把前(N)次走过的坐标写下来,记((x_N,y_N) = (a,b)) 显然有第(i)轮走过的坐标只是第一轮基础上加上(((i - 1)a,(i-1)b))
放到平面上,可以看成有至多(N)条本质不同,斜率相同的直线
计数的实际上是对某条直线找到起点,横坐标每次走(a)的点
冲突是因为某条直线上可以有多个起点,这些点两两同余,因此可在取模意义下计数,只需对直线上每个点计算模意义下的"距离"(几步可以走到),排序后对(K)取min即可,注意点的同余,可以用pair类比一维的整数
两个简化代码的技巧:若模数为0,需要特殊讨论,不妨对(b) 取swap;若(a <0),不妨对(y)轴对称,显然不影响答案
代码
int main(){
scanf("%s",s);
ll k;
scanf("%lld",&k);
int len = strlen(s);
int a = 0;
int b = 0;
ll ans = 0;
vector<pii> v(len + 1);
v[0] = make_pair(0,0);
map<pii,int> t;
t[make_pair(0,0)] = 1;
int tmp = 1;
for(int i = 0;i < len;i++){
if(s[i] == 'L') a--;
else if(s[i] == 'R') a++;
else if(s[i] == 'U') b--;
else b++;
v[i + 1] = make_pair(a,b);
if(!t.count(v[i + 1])) t[v[i + 1]] = 1,tmp++;
}
if(!a && !b) {
printf("%d",tmp);
return 0;
}
if(!a) {
swap(a,b);
for(int i = 1;i <= len;i++)
swap(v[i].fi,v[i].se);
}
if(a < 0) {
a = -a;
for(int i = 1;i <= len;i++)
v[i].fi = -v[i].fi;
}
sort(all(v));
map<pii,int> mp;
for(int i = len;i >= 0;i--) {
int lst = v[i].fi;
v[i].fi = (v[i].fi % a + a) % a;
int t = (lst - v[i].fi) / a;
v[i].se -= (ll)t * b;
if(mp.count(v[i]))
ans += min(k,(ll)mp[v[i]] - t);
else ans += k;
mp[v[i]] = t;
}
cout << ans;
}