CF1236D Alice&Doll
题目描述
有一个机器人在一个(n imes m)的有(k)个障碍网格上移动,上北下南左西右东。
它一开始在第一行第一列,面朝东边。它在每个格子上可以右转一次或不右转,然后会向面向的方向走一步。
问能不能将所有不是障碍的格子都走恰好一遍。(n,m,kleq10^5)
题解
会发现每个格只能右转一次,所以最后走出的路径大概这样:
-------------------
* |
* |
|----------* |
| *-----| |
*------------------
手动模拟这个过程就行。
注意考虑在一开始就拐弯的情况,不然会FST。
代码
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#define LL long long
#define rep(i,x,y) for(int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(int i=(x);i>=(y);--i)
#define view(u,k) for(int k=fir[u];~k;k=nxt[k])
#define maxn 100007
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
void write(int x)
{
char ch[20];int f=0;
if(!x){putchar('0'),putchar('
');return;}
if(x<0)putchar('-'),x=-x;
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar('
');
}
vector<int>nd[2][maxn];
int p[2][maxn],n,m,k,mx[4],now,f;
bool cmpy(int x,int y){return p[1][x]<p[1][y];}
bool cmpx(int x,int y){return p[0][x]<p[0][y];}
int getp(int rc,int k,int x,int f)
{
if(nd[rc][k].size()==0)return -1;
int l=0,r=nd[rc][k].size()-1,ans=-1;
if(f==0&&p[rc^1][nd[rc][k][l]]>x)return -1;
if(f==1&&p[rc^1][nd[rc][k][r]]<x)return -1;
while(l<=r)
{
int mid=l+r>>1;
if((!f&&p[rc^1][nd[rc][k][mid]]<=x)||(f&&p[rc^1][nd[rc][k][mid]]>=x))
{
if(!f)ans=max(ans,mid),l=mid+1;
else ans=ans==-1?mid:min(ans,mid),r=mid-1;
}
else
{
if(!f)r=mid-1;
else l=mid+1;
}
}
if(ans!=-1)ans=nd[rc][k][ans];
return ans;
}
int main()
{
n=read(),m=read(),k=read();mx[0]=m,mx[1]=n,mx[2]=mx[3]=1;
rep(i,1,k){p[0][i]=read(),p[1][i]=read(),nd[0][p[0][i]].push_back(i),nd[1][p[1][i]].push_back(i);if(p[0][i]==1&&p[1][i]==2)f=1;}
rep(i,1,n)sort(nd[0][i].begin(),nd[0][i].end(),cmpy);
rep(i,1,m)sort(nd[1][i].begin(),nd[1][i].end(),cmpx);
int px=1,py=1;LL sum=(LL)n*m-1;
if(m==1)f=1;
if(f)now=1,mx[0]=1;
while(1)
{
int pre=now-1;if(pre==-1)pre=3;
if(now==0)
{
int x=getp(0,px,py,1);
if(x!=-1)x=min(mx[now],p[1][x]-1);
else x=mx[now];
if(x==py)break;
sum-=(LL)(x-py);
mx[pre]=px+1,py=x;
}
else if(now==1)
{
int x=getp(1,py,px,1);
if(x!=-1)x=min(mx[now],p[0][x]-1);
else x=mx[now];
if(x==px)break;
sum-=(LL)(x-px);
mx[pre]=py-1,px=x;
}
else if(now==2)
{
int x=getp(0,px,py,0);
if(x!=-1)x=max(mx[now],p[1][x]+1);
else x=mx[now];
if(x==py)break;
sum-=(LL)(py-x);
mx[pre]=px-1,py=x;
}
else
{
int x=getp(1,py,px,0);
if(x!=-1)x=max(mx[now],p[0][x]+1);
else x=mx[now];
if(x==px)break;
sum-=(LL)(px-x);
mx[pre]=py+1,px=x;
}
now=(now+1)%4;
}
if(sum==(LL)k)puts("Yes");
else puts("No");
return (0-0);
}
CF1236E Alice&UnfairGame
题目描述
A和B两个人在玩游戏。有编号为1到(n)的(n)个箱子按编号顺序排成一排,有一个箱子里有东西。
游戏要进行(m)轮,每轮的流程是:A先把东西移动到和它相邻的箱子里,然后B猜哪个箱子里有东西。一旦B猜中了,游戏就会结束,并且B获胜。
若游戏进行完了第(m)轮,则A还可以把东西移动到和它相邻的箱子里一次,之后A胜利。
已知(n,m),和B在每一轮会猜测的箱子编号(a_1,...,a_m),问有多少对((x,y))满足游戏开始前东西在(x)号箱子,游戏结束时东西在(y)号箱子,且A胜利。
一行题解
对于同一个起点,合法终点连续=>在有障碍物的平面上行走=>在某时刻走到某点的“人”的起点连续=>线段树维护区间加减
题解
这个人讲得很清楚。
发现在A知道B的猜测序列时,除非(n=1),A总有办法赢。
具体的方式是:从随便一个位置出发,如果这一轮B猜的是左边相邻的箱子就往右走或不走,右边同理,如果B猜的是当前箱子就往右或左走,如果都不是就往右或往左或不走。
按这种移动方式,会发现从每个位置出发,能使A赢的结束位置一定是连续的。
所以可以通过求从每个点出发能走到的使A赢的最左、最右位置。
先考虑最右位置。
假设从(x)出发,当前位置为(p),时刻为(t),那么为了尽可能往右走,应该当且仅当(a_t=p+1)时不会走,其他时候都会向右走。
可以把这个过程看成在平面上行走:以时刻为横坐标,以位置为纵坐标,一个人一开始在((0,x))出发,对于每个时刻,如果他右上方没有障碍物就往右上走,反之往右走。
这样是(O(n^2))的。
考虑把所有“人”放在一块考虑:有(n)个人在平面上行走,(i)号人从((0,i))出发,每个人的走法和上面一样。
发现人们走出的路径可能会合并,但不会交叉。也就是说,某时刻在某位置的人们的坐标是连续的,某时刻人们的坐标按编号是单调不降的。
对于坐标为((i,a_i))的障碍物,会挡住走到((i-1,a_i-1))的“人”们。可以二分出这一段“人”的编号区间。在下一个时刻,除了这个编号区间的人以外,所有人纵坐标+1。
可以用线段树维护当前人们走到的位置的纵坐标。
注意人们不能走出去,因此可以设置((1,n+1),(2,n+1),...,(m,n+1))这些额外的障碍物。
对于最左位置,可以看成把序列翻转后的最右位置。
时间复杂度(O((n+m)logspace n))。
代码
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#define rep(i,x,y) for(register int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(register int i=(x);i>=(y);--i)
#define view(u,k) for(int k=fir[u];~k;k=nxt[k])
#define LL long long
#define maxn 100007
#define ls (u<<1)
#define rs (u<<1|1)
#define mi ((l+r)>>1)
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
void write(LL x)
{
if(x==0){putchar('0'),putchar('
');return;}
int f=0;char ch[20];
if(x<0)putchar('-'),x=-x;
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar('
');
return;
}
int n,a[maxn],m,ans[2][maxn];
LL Ans;
int mn[maxn<<2],mx[maxn<<2],mk[maxn<<2];
void pu(int u){mn[u]=min(mn[ls],mn[rs]),mx[u]=max(mx[ls],mx[rs]);}
void build(int u,int l,int r)
{
mk[u]=0;
if(l==r){mx[u]=mn[u]=l;return;}
build(ls,l,mi),build(rs,mi+1,r),pu(u);return;
}
void mark(int u,int k){mn[u]+=k,mx[u]+=k,mk[u]+=k;}
void pd(int u){if(mk[u])mark(ls,mk[u]),mark(rs,mk[u]),mk[u]=0;}
void add(int u,int l,int r,int x,int y,int k)
{
if(x<=l&&r<=y){mark(u,k);return;}
pd(u);
if(x<=mi)add(ls,l,mi,x,y,k);
if(y>mi)add(rs,mi+1,r,x,y,k);
pu(u);return;
}
int askl(int u,int l,int r,int x)//max,<x
{
if(l==r)return mx[u]<x?l:l-1;
pd(u);
if(mn[rs]<x)return askl(rs,mi+1,r,x);
return askl(ls,l,mi,x);
}
int askr(int u,int l,int r,int x)//min,>x
{
if(l==r)return mx[u]>x?l:l+1;
pd(u);
if(mx[ls]>x)return askr(ls,l,mi,x);
return askr(rs,mi+1,r,x);
}
int ask(int u,int l,int r,int x)
{
if(x<=l&&r<=x){return mx[u];}
pd(u);
if(x<=mi)return ask(ls,l,mi,x);
return ask(rs,mi+1,r,x);
}
void work(int f)
{
build(1,1,n);
rep(i,1,m+1)
{
int l2=askl(1,1,n,n),r2=n;
if(i<=m)
{
int l=askl(1,1,n,a[i]-1),r=askr(1,1,n,a[i]-1);
if(l+1<=r-1)add(1,1,n,l+1,r-1,-1);
}
mk[1]++;
if(l2+1<=r2)add(1,1,n,l2+1,r2,-1);
}
rep(i,1,n)ans[f][i]=ask(1,1,n,i);
}
int main()
{
n=read(),m=read();
rep(i,1,m)a[i]=read();
work(0);
rep(i,1,m)a[i]=n-a[i]+1;
work(1);
reverse(ans[1]+1,ans[1]+n+1);
rep(i,1,n)ans[1][i]=n-ans[1][i]+1;
rep(i,1,n)Ans+=max(0ll,(LL)(ans[0][i]-ans[1][i]+1));
write(Ans);
return 0;
}