zoukankan      html  css  js  c++  java
  • 并不对劲的CF1236D&E:Alice&Doll&UnfairGame

    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;
    }
    
  • 相关阅读:
    ZOJ 3765 Lights (zju March I)伸展树Splay
    UVA 11922 伸展树Splay 第一题
    UVALive 4794 Sharing Chocolate DP
    ZOJ 3757 Alice and Bod 模拟
    UVALive 3983 捡垃圾的机器人 DP
    UVA 10891 SUM游戏 DP
    poj 1328 Radar Installatio【贪心】
    poj 3264 Balanced Lineup【RMQ-ST查询区间最大最小值之差 +模板应用】
    【转】RMQ-ST算法详解
    poj 3083 Children of the Candy Corn 【条件约束dfs搜索 + bfs搜索】【复习搜索题目一定要看这道题目】
  • 原文地址:https://www.cnblogs.com/xzyf/p/11713734.html
Copyright © 2011-2022 走看看