题目链接:http://codeforces.com/contest/611/problem/F
题意:有一个方块矩阵h*w(h,w<=500000),一个机器人在一个控制序列(n<=500000)的循环作用下可以在矩阵中在上、下、左、右方向上移动,移动一次花费一分钟时间,每次将要超出矩阵范围时机器人停止运动。现将矩阵中的每一个方格作为起始点,即机器人以某一个方格为起点运动停止后,将其放置在下一个方格中开始运动,问多少时间可以将这个过程完成。
解题思路:脑洞题,记录每一次移动时移动的步长,移动一步时可能会有某一行或某一列被消去,那么这一行或列对答案的贡献就是面积乘以步长,维护当前矩阵的大小(左上和右下的坐标),不断模拟机器人移动,直到矩阵被完全消去。
实现细节:
1.建立坐标系,列数增加方向代表x轴正方向,行数增加方向代表y轴正方向,当前矩阵范围为(min_x,min_y)~(max_x,max_y);
2.记录当前x和y的变化值dx和dy,维护x和y的当前移动的最大范围【mx1,mx2】和【my1,my2】;
3.对于每一个移动指令,根据dx和【mx1,mx2】判断是否会有列消去,根据dy和【my1,my2】判断是否会有列消去(具体看代码);
4.当模拟一次控制序列后,如果矩阵没有被完全消去且dx=dy=0,那么这个矩阵无法被消去,输出-1;
以上是纯模拟过程,复杂度最大可以达到O【(h+w)*n】,导致TLE,所以要优化。
优化方法:
O(n)时间模拟两次,记录第二次模拟时发生消去的位置,那么可以发现以后的模拟中消去操作都发生在这些位置,然后循环模拟这些位置的操作,直至矩阵被完全消去。复杂度为O(h+w+n)。
注意,步长最大能够达到max(h,w)*n,要用long long。
此题还有一个挑战版本:题意不变,n的范围不变,但(h,w<=1e9),表示目前还没想出来^_^,留个坑。
以下为AC代码(CF上标程只有60行,而我写了120行):
MY_self:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <map>
#include <stack>
#include <queue>
using namespace std;
typedef long long LL;
const int mod=1e9+7;
char s[500005];
vector<int> use;
int n;
int max_x,min_x,max_y,min_y;
int dx,dy;
int mx1,mx2,my1,my2,is;
int ans;
LL cnt;
void del(bool mem){
for(int i=0;i<n;i++){
if(max_x<min_x||max_y<min_y)
break;
cnt++;
if(s[i]=='R'){
if(dx>=mx1){
if(mem) use.push_back(i);
mx1++;
max_x--;
ans=(ans+(max_y-min_y+1)*cnt%mod)%mod;
}
dx++;
}
if(s[i]=='L'){
if(dx<=mx2){
if(mem) use.push_back(i);
mx2--;
min_x++;
ans=(ans+(max_y-min_y+1)*cnt%mod)%mod;
}
dx--;
}
if(s[i]=='D'){
if(dy>=my1){
if(mem) use.push_back(i);
my1++;
max_y--;
ans=(ans+(max_x-min_x+1)*cnt%mod)%mod;
}
dy++;
}
if(s[i]=='U'){
if(dy<=my2){
if(mem) use.push_back(i);
my2--;
min_y++;
ans=(ans+(max_x-min_x+1)*cnt%mod)%mod;
}
dy--;
}
}
}
int main (){
int h,w;
while(scanf("%d%d%d",&n,&h,&w)!=EOF){
scanf("%s",s);
max_x=w,min_x=1,max_y=h,min_y=1;
dx=0,dy=0;
mx1=0,mx2=0,my1=0,my2=0;
ans=0,cnt=0,is=1;
del(0); //第一次删除,无记忆操作
if(is&&dx==0&&dy==0) is=0;
else{
use.clear();
del(1); //第二次删除,有记忆操作
int cur=0;
int first=1;
while(max_x>=min_x && max_y>=min_y){ //循环操作(优化部分)
if(cur==0){
if(first==0)
cnt+=n-1-use[use.size()-1]+use[0]+1;
else {
cnt+=use[0]+1;
first=0;
}
}
else cnt+=use[cur]-use[cur-1];
switch(s[use[cur]]){
case 'R':
max_x--;
ans=(ans+(max_y-min_y+1)*cnt%mod)%mod;
break;
case 'L':
min_x++;
ans=(ans+(max_y-min_y+1)*cnt%mod)%mod;
break;
case 'U':
min_y++;
ans=(ans+(max_x-min_x+1)*cnt%mod)%mod;
break;
case 'D':
max_y--;
ans=(ans+(max_x-min_x+1)*cnt%mod)%mod;
break;
}
if(cur==((int)use.size()-1)) cur=0;
else cur++;
}
}
if(is!=0)
printf("%d\n",ans);
else
printf("-1\n");
}
return 0;
}
CF标程:
// Cleaning Robot (F), by Errichto
// AC, O(w+h+n)
#include<bits/stdc++.h>
using namespace std;
#define FOR(i,a,b) for(int i = (a); i <= (b); ++i)
#define RI(i,n) FOR(i,1,(n))
#define REP(i,n) FOR(i,0,(n)-1)
typedef long long ll;
const int nax = 5e5 + 5;
const int mod = 1e9 + 7;
char sl[nax];
int n;
int ans;
int low[2], high[2], curr[2], dimension[2];
bool f(ll moves, bool cheating) {
if(moves != 0 && moves % n == 0 && curr[0] == 0 && curr[1] == 0) {
puts("-1");
exit(0);
}
char type = sl[moves%n];
if(type == 'L') cheating ? curr[0] = high[0]+1 : ++curr[0];
else if(type == 'R') cheating ? curr[0] = low[0]-1 : --curr[0];
else if(type == 'U') cheating ? curr[1] = high[1]+1 : ++curr[1];
else if(type == 'D') cheating ? curr[1] = low[1]-1 : --curr[1];
else assert(false);
bool something_happened = false;
REP(i, 2) {
if(curr[i] < low[i] || curr[i] > high[i]) {
ans = (ans + (moves + 1) % mod * dimension[i^1]) % mod;
--dimension[i];
something_happened = true;
}
if(curr[i] < low[i]) low[i] = curr[i];
if(curr[i] > high[i]) high[i] = curr[i];
}
return something_happened;
}
bool inGame() { return dimension[0] > 0 && dimension[1] > 0; }
int main() {
scanf("%d%d%d", &n, &dimension[1], &dimension[0]);
scanf("%s", sl);
for(ll moves = 0; inGame() && moves < n; ++moves)
f(moves, false);
vector<int> w;
for(ll moves = n; inGame() && moves < 2 * n; ++moves)
if(f(moves, false)) w.push_back((int) moves % n);
for(ll k = 2; inGame(); ++k)
for(int moves : w) if(inGame())
f(k*n + moves, true);
printf("%d\n", ans);
return 0;
}