Day 9
125
T1 走格子
Solition
伪装成搜索的最短路233
读题看出来走的方式有两种:普通移动和传送门移动。
普通移动就枚举每个点,朝4个方向建边。
麻烦的是传送门移动。
我们注意到每个点上下左右都一定会有一个传送门,我们可以对他们之间的任意一个建一个传送门。
但是要使用这些个传送门是有代价的,我们需要普通移动到最近的墙放个门,移动到想要去的门那里。
预处理出每个’.‘点到最近的’#'的距离,那么一个点到一个传送门的代价就是它到最近的墙的距离。bfs预处理即可。
#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
typedef pair<int, int> p;
const int N = 510;
const int INF = 0x3f3f3f3f;
const int dx[4]={-1, 1, 0, 0};
const int dy[4]={0, 0, -1, 1};
char read(){
char ch=getchar();
while(ch!='.' && ch!='#' && ch!='C' && ch!='F'){
ch=getchar();
}
return ch;
}
int n,m,sx,sy,ex,ey;
struct edge{
int u,to,nxt,w;
}e[(N*N)<<4];
char a[N][N];
int cnte,head[N*N],dis[N*N];
bool vis[N*N];
int d[N][N],cntw;//d[i][j]是(i, j)到最近的墙的距离
p wall[N*N];//是墙的点
void add(int u,int v,int w){
e[++cnte].to=v;
e[cnte].nxt=head[u];
e[cnte].w=w;
e[cnte].u=u;
head[u]=cnte;
}
int id(int x,int y){
return m*(x-1)+y;
}
void bfs(){
queue<p> q;
for(int i=1; i<=cntw; i++){
q.push(wall[i]);
}
while(!q.empty()){
p u=q.front();
q.pop();
int ux=u.first;
int uy=u.second;
for(int i=0; i<4; i++){
int nx=ux+dx[i];
int ny=uy+dy[i];
if(nx>=1 && nx<=n && ny>=1 && ny<=m && !d[nx][ny] && a[nx][ny]!='#'){
d[nx][ny]=d[ux][uy]+1;
q.push(make_pair(nx, ny));
}
}
}
}
void spfa(){
memset(dis, 0x3f, sizeof dis);
queue<int> q;
q.push(id(sx, sy));
vis[id(sx, sy)]=1;
dis[id(sx, sy)]=0;
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u]; i; i=e[i].nxt){
int v=e[i].to;
int w=e[i].w;
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
if(!vis[v]){
q.push(v);
vis[v]=1;
}
}
}
}
}
int main(){
freopen("cell.in","r",stdin);
freopen("cell.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++){
for(int j=1; j<=m; j++){
a[i][j]=read();
if(a[i][j]=='C'){
sx=i;
sy=j;
}
else if(a[i][j]=='F'){
ex=i;
ey=j;
}
else if(a[i][j]=='#'){
wall[++cntw]=make_pair(i, j);
}
}
}
bfs();
for(int i=2; i<n; i++){
for(int j=2; j<m; j++){
if(a[i][j]=='#'){
continue;
}
for(int k=0; k<4; k++){
int nx=i+dx[k];
int ny=j+dy[k];
if(nx>=1 && nx<=n && ny>=1 && ny<=m && a[nx][ny]!='#'){
add(id(i, j), id(nx, ny), 1);
}
}
}
}
for(int i=2; i<n; i++){
for(int j=2; j<m; j++){
if(a[i][j]=='#'){
continue;
}
for(int k=0; k<4; k++){
int nx=i+dx[k];
int ny=j+dy[k];
while(nx>=1 && nx<=n && ny>=1 && ny<=m && a[nx][ny]!='#'){
nx+=dx[k];
ny+=dy[k];
}
nx-=dx[k];
ny-=dy[k];
add(id(i, j), id(nx, ny), d[i][j]);
}
}
}
spfa();
if(dis[id(ex, ey)]==INF){
printf("no\n");
}
else{
printf("%d\n",dis[id(ex, ey)]);
}
return 0;
}
T2 扭动的树
AFK
T3 旋转子段
Solution
1.如果一个序列[l,r]能成为最终答案,那么l,r两点对答案一定有贡献,不然我们可以取[l+1,r-1]作为答案。
2.我们记数字i的位置为a[i]。
如果数字i与数字j经过旋转能到位,一定有a[i]+i=a[j]+j,且a[i]+i为中心点的坐标(记1左边为1,1为2,1和2中间为3,2为4…)。
对于每个数,计算出a[i]+i,将 i push进入数组v[i+a[i]]。再进行根据该点到中心的距离sort一遍,枚举端点,记录贡献,还要加上不旋转区间本来就在自己位置上的点的贡献。用前缀和统计即可。
#include <cstdio>
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
#include <ctime>
using namespace std;
const int N = 100010;
int n,a[N],s[N],cen,ans;
vector <int> v[N<<1];
bool cmp(int x,int y){
return abs((x<<1)-cen)<abs((y<<1)-cen);
}
int main(){
freopen("rotate.in","r",stdin);
freopen("rotate.out","w",stdout);
scanf("%d",&n);
for(int i=1; i<=n; i++){
scanf("%d",&a[i]);
s[i]=s[i-1]+(a[i]==i);
v[a[i]+i].push_back(i);
}
for(cen=1; cen<=(n<<1); cen++){
sort(v[cen].begin(), v[cen].end(), cmp);
int siz=v[cen].size();
for(int i=0; i<siz; i++){
int tmp1=v[cen][i];
int tmp2=cen-v[cen][i];
int L=min(tmp1, tmp2);
int R=max(tmp1, tmp2);
ans=max(ans, i+1+s[L-1]+(s[n]-s[R]));
}
}
printf("%d\n",ans);
return 0;
}