zoukankan      html  css  js  c++  java
  • AtCoder Grand Contest 033 题解

    传送门

    我比赛的时候怕不是在睡觉啊……

    (A Darker and Darker)

    我是不是想得太复杂了……根本没必要像我这样做吧……

    首先问题可以转化成令(p_{i,j})表示到((i,j))这个白点最近的的黑点的距离,求(max{p_{i,j}})。而答案显然是可以二分的

    对于某一个白点,我们怎么判断到它的距离不超过(mid)的范围内是否有黑点呢?这里的距离是曼哈顿距离,非常麻烦,我们坐标系变换一下把它转成一个切比雪夫距离,那么到它的距离不超过(mid)的范围就可以化成一个矩形了,预处理矩形的前缀和减一减就可以了

    //minamoto
    #include<bits/stdc++.h>
    #define R register
    #define inline __inline__ __attribute__((always_inline))
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    const int N=4005,M=1000;
    char s[N][N];int n,m,l,r,ans,mid,mp[N][N];
    inline int calc(R int x,R int y){
    	R int dx=x,dy=y,xx,yy;x=dx+dy+M,y=dx-dy+M;
    	dx=x+mid,dy=y+mid,xx=x-mid,yy=y-mid;
    	xx=max(xx,2+M),yy=max(yy,1-m+M),dx=min(dx,n+m+M),dy=min(dy,n-1+M);
    //	puts("qwq");
    //	printf("%d %d %d %d %d %d
    ",x-M,y-M,xx-M,yy-M,dx-M,dy-M);
    	return mp[dx][dy]-mp[dx][yy-1]-mp[xx-1][dy]+mp[xx-1][yy-1];
    }
    bool ck(){
    	fp(i,1,n)fp(j,1,m)if(s[i][j]=='.'&&!calc(i,j))return false;
    	return true;
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	scanf("%d%d",&n,&m),l=0,r=n+m,ans=0;
    	fp(i,1,n)scanf("%s",s[i]+1);
    	fp(i,1,n)fp(j,1,m)mp[i+j+M][i-j+M]=(s[i][j]=='#');
    	fp(i,2+M,n+m+M)fp(j,1-m+M,n-1+M)mp[i][j]+=mp[i-1][j]+mp[i][j-1]-mp[i-1][j-1];
    //	mid=1,printf("%d
    ",ck());
    	while(l<=r){
    		mid=(l+r)>>1;
    		ck()?(ans=mid,r=mid-1):l=mid+1;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    (B LRUD Game)

    发现左右的移动和上下的移动没有关系,那么我们分开考虑,以左右为例,上下同理

    我们枚举是从左边出边界还是右边出边界,如果是左边出边界,那么一个人只会选(L),另一个人只会选(R),注意选(R)的那个人别自己(R)出边界。右边出边界同理

    //minamoto
    #include<bits/stdc++.h>
    #define R register
    #define inline __inline__ __attribute__((always_inline))
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    char buf[1<<21],*p1=buf,*p2=buf;
    inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
    int read(){
        R int res,f=1;R char ch;
        while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
        for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
        return res*f;
    }
    int read(char *s){
    	R int len=0;R char ch;while(((ch=getc())>'Z'||ch<'A'));
    	for(s[++len]=ch;(ch=getc())>='A'&&ch<='Z';s[++len]=ch);
    	return s[len+1]='',len;
    }
    const int N=2e5+5;
    char s[N],t[N];int n,m,len,sx,sy;
    bool calc(int n,int x,char a,char b){
    	int p=x;
    	fp(i,1,len){
    		if(s[i]==b)++p;if(p>n)return false;
    		if(t[i]==a)p=max(p-1,1);
    	}
    	p=x;
    	fp(i,1,len){
    		if(s[i]==a)--p;if(p<1)return false;
    		if(t[i]==b)p=min(p+1,n);
    	}
    	return true;
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	n=read(),m=read(),len=read(),sx=read(),sy=read(),read(s),read(t);
    	puts(calc(n,sx,'U','D')&&calc(m,sy,'L','R')?"YES":"NO");
    	return 0;
    }
    

    (C moving coins)

    我们考虑一次以(u)为根的操作,树上的所有节点中有且只有叶子节点会变得不再有硬币,而且发现此后它们再也不会有硬币,我们可以简单地认为所有叶子节点都被删除了

    那么问题转化为每次选定一个根并删掉所有叶子,删完整棵树的人赢

    我们考虑这棵树的直径(L),假设(L>2),根据定理,如果(L)为奇数那么所有直径过同一个点,如果(L)为偶数所有直径过同一条边,那么我们肯定能找到一个点,使得选其为根操作之后(L-=2)。而如果我们选在其中一条直径的一个叶子端点上,那么(L-=1)

    而对于(L=1)的情况先手必胜,(L=2)的情况先手必败

    于是问题可以转化为每次可以令(L)减一或减二,先让它减到(2)的赢

    这应该是个比较经典的模型了,大家小时候应该都玩过,如果(Lequiv 2pmod{3})的时候后手胜,否则先手胜

    于是求个直径就行了

    //minamoto
    #include<bits/stdc++.h>
    #define R register
    #define inline __inline__ __attribute__((always_inline))
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    char buf[1<<21],*p1=buf,*p2=buf;
    inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
    int read(){
        R int res,f=1;R char ch;
        while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
        for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
        return res*f;
    }
    const int N=2e5+5;
    struct eg{int v,nx;}e[N<<1];int head[N],tot;
    inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
    int mx[N],n,res;
    void dfs(int u,int fa){
    	mx[u]=1;
    	go(u)if(v!=fa)dfs(v,u),cmax(res,mx[v]+mx[u]),cmax(mx[u],mx[v]+1);
    	cmax(res,mx[u]);
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	n=read();
    	for(R int i=1,u,v;i<n;++i)u=read(),v=read(),add(u,v),add(v,u);
    	dfs(1,0);
    	puts(res%3==2?"Second":"First");
    	return 0;
    }
    

    (D Complexity)

    首先有一个结论,(A_{ij})(O(log(H+W)))级别的。因为我们横竖对半分之后一定能把它分成只有一个格子的情况

    那么我们只要模拟一下就行了,枚举(A_{ij}),并算出(fl_{i,j,k})表示对于((i,j))为右上角,((k,j))为右下角的矩形此时横向的长度最大是多少,以及(fu_{i,j,k})表示以((i,j))为左下角,((i,k))为右下角的矩形此时纵向的长度最大为多少。只要某一个时刻整个矩形都被包括了那么就输出答案

    //minamoto
    #include<bits/stdc++.h>
    #define R register
    #define inline __inline__ __attribute__((always_inline))
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    const int N=205;
    int fl[N][N][N],fu[N][N][N],gl[N][N][N],gu[N][N][N];
    char mp[N][N];int L[N][N],U[N][N],n,m;
    int main(){
    //	freopen("testdata.in","r",stdin);
    	scanf("%d%d",&n,&m);
    	fp(i,1,n){
    		scanf("%s",mp[i]+1);
    		fp(j,1,m){
    			L[i][j]=(mp[i][j]==mp[i][j-1])?L[i][j-1]+1:1;
    			U[i][j]=(mp[i][j]==mp[i-1][j])?U[i-1][j]+1:1;
    		}
    	}
    	fp(x,1,n)fp(y,1,m){
    		for(R int xx=x,mn=233;mp[x][y]==mp[xx][y];++xx)cmin(mn,L[xx][y]),fl[x][y][xx]=mn;
    		for(R int yy=y,mn=233;mp[x][y]==mp[x][yy];++yy)cmin(mn,U[x][yy]),fu[x][y][yy]=mn;
    	}
    	for(int res=0;233;++res){
    		if(fl[1][m][n]==m||fu[n][1][m]==n)return printf("%d
    ",res),0;
    		memcpy(gl,fl,sizeof(fl)),memcpy(gu,fu,sizeof(fu));
    		fp(x,1,n)fp(y,1,m){
    			fp(xx,x,n)fl[x][y][xx]+=gl[x][y-gl[x][y][xx]][xx];
    			fp(yy,y,m)fu[x][y][yy]+=gu[x-gu[x][y][yy]][y][yy];
    		}
    		fp(x,1,n)fp(y,1,m){
    			for(R int xx=x,X=1;xx<=n;++xx,++X)
    				for(R int yy=y-fl[x][y][xx]+1;yy<=y&&fu[xx][yy][y]<X;++yy)
    					fu[xx][yy][y]=X;
    			for(R int yy=y,Y=1;yy<=m;++yy,++Y)
    				for(R int xx=x-fu[x][y][yy]+1;xx<=x&&fl[xx][yy][x]<Y;++xx)
    					fl[xx][yy][x]=Y;
    		}
    	}
    	return 0;
    }
    

    (E Go around a Circle)

    完了数数都数不来了……

    我们分情况考虑,如果全都是同一种颜色,假设全都为(R),那么连续两段蓝色的圆弧肯定不合法,问题转化为一个长度为(n)的序列,不允许有两个相邻的蓝色,首尾也不许同时蓝色的方案数。记一下(dp_{0,1,2,3})分别表示开始是否为蓝色,当前最后一个是否为蓝色的方案数,转移显然

    如果不是同一种颜色,假设开头的颜色为(R),那么依然不允许存在两端相邻蓝色圆弧。然后还有两个结论

    • 整个序列一定是由若干段组成的,其中每一段都形如连续奇数个红色+一个蓝色

    • 每一段序列的长度都有一个限制,不能超过(L)

    先考虑第一个结论,如果连续红色的个数为偶数,我们不妨设序列最前面有连续(k)(R),如果(k)是奇数,把起始点设为这段红色的结尾就(gg),如果(k)是偶数,把起始点设为这段红色结尾前一个依然(gg),所以只有红色个数是奇数合法

    第二个结论,对于开头的连续(k)(R),如果(k)是偶数,则最多只能放连续(k+1)个红色,如果(k)是奇数则最多只能放连续(k)个。如果这(k)个连续(R)不在开头,那么它们前面是一个(B),也就是说我们现在在一段红色的一个端点处。如果(k)是偶数,显然可以走回来,如果(k)是奇数,那么这段连续红色的个数不能超过(k)

    那么我们预处理出(dp_i)表示长度为(i)的序列的合法方案数,发现奇数的位置肯定全都是红色,所以我们只要考虑偶数位置怎么放就行了,转移也比较显然

    最后是旋转导致的多种方案数,我们枚举第一个蓝色和最后一个蓝色之间的距离,乘上对应的系数就好了

    //minamoto
    #include<bits/stdc++.h>
    #define R register
    #define inline __inline__ __attribute__((always_inline))
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    const int N=2e5+5,P=1e9+7;
    inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
    inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
    inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
    inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
    char s[N];int f[N],dp[2][5],n,m,res,t;
    void solve1(){
    	dp[0][0]=dp[0][3]=1,t=0;
    	for(R int i=1;i<n;++i,t^=1){
    		dp[t^1][0]=dp[t^1][1]=dp[t^1][2]=dp[t^1][3]=0;
    		dp[t^1][0]=add(dp[t][0],dp[t][1]),
    		dp[t^1][1]=dp[t][0],
    		dp[t^1][2]=add(dp[t][2],dp[t][3]),
    		dp[t^1][3]=dp[t][2];
    	}
    	res=add(dp[t][0],add(dp[t][1],dp[t][2]));
    	printf("%d
    ",res);
    }
    void solve2(){
    	int lim=n,cur=0,las=m;
    	while(s[las]=='R')--las;
    	fd(i,las,1)if(s[i]=='B'){
    		if(cur&1)cmin(lim,cur+1);
    		cur=0;
    	}else ++cur;
    	cmin(lim,cur+1+(cur&1^1));
    	f[0]=f[2]=1;
    	for(R int i=4;i<=n;i+=2){
    		f[i]=mul(f[i-2],2);
    		if(i>=lim+2)f[i]=dec(f[i],f[i-(lim+2)]);
    	}
    	res=0;
    	for(R int i=2;i<=lim;i+=2)upd(res,mul(f[n-i],i));
    	printf("%d
    ",res);
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	scanf("%d%d%s",&n,&m,s+1);
    	if(s[1]=='B')fp(i,1,m)s[i]^='B'^'R';
    	bool flag=0;
    	fp(i,1,m)if(s[i]=='B'){flag=1;break;}
    	if(!flag)solve1();else solve2();
    	return 0;
    }
    

    (F)题咕了

  • 相关阅读:
    继承与多态
    String作业
    课后作业及动手动脑
    作业3
    动手动脑
    实验任务四
    动手动脑
    课堂练习01
    《大道至简》第一章读后感伪代码
    C++常用编程关键字
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/10834733.html
Copyright © 2011-2022 走看看