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\n",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\n",ck());
while(l<=r){
mid=(l+r)>>1;
ck()?(ans=mid,r=mid-1):l=mid+1;
}
printf("%d\n",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]='\0',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\)的赢
这应该是个比较经典的模型了,大家小时候应该都玩过,如果\(L\equiv 2\pmod{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\n",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\n",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\n",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\)题咕了
AtCoder Grand Contest 033 题解的更多相关文章
- AtCoder Grand Contest 017 题解
A - Biscuits 题目: 给出 \(n\) 个物品,每个物品有一个权值. 问有多少种选取方式使得物品权值之和 \(\bmod\space 2\) 为 \(p\). \(n \leq 50\) ...
- Atcoder Grand Contest 054 题解
那天晚上由于毕业晚会与同学吃饭喝酒没打 AGC,第二天稍微补了下题,目前补到了 E,显然 AGC 的 F 对于我来说都是不可做题就没补了(bushi A 简单题,不难发现如果我们通过三次及以上的操作将 ...
- AtCoder Grand Contest 030题解
第一次套刷AtCoder 体验良好 传送门 Poisonous Cookies cout<<b+min(c,a+b+); Tree Burning 难度跨度有点大啊 可以证明当第一次转向之 ...
- AtCoder Grand Contest 031题解
题面 传送门 题解 比赛的之后做完\(AB\)就开始发呆了--简直菜的一笔啊-- \(A - Colorful\ Subsequence\) 如果第\(i\)个字母选,那么它前面任意一个别的字母的选择 ...
- AtCoder Grand Contest 039 题解
传送门 \(A\) 首先只有一串的情况下,遇到相同的肯定是改后面那一个最优,然后两串的话可能要分奇偶讨论一下 //quming #include<bits/stdc++.h> #defin ...
- AtCoder Grand Contest 017题解
传送门 \(A\) 直接转移就是了 typedef long long ll; const int N=55; ll f[N][2];int a[N],n,p; int main(){ scanf(& ...
- AtCoder Grand Contest 015题解
传送门 \(A\) 找到能达到的最大的和最小的,那么中间任意一个都可以被表示出来 typedef long long ll; int n,a,b;ll res; int main(){ scanf(& ...
- AtCoder Grand Contest 038 题解
传送门 这场表现的宛如一个\(zz\) \(A\) 先直接把前\(b\)行全写成\(1\),再把前\(a\)列取反就行 const int N=1005; char mp[N][N];int n,m, ...
- AtCoder Grand Contest 014题解
传送门 \(A\) 首先大力猜测一下答案不会很大,所以次数大于\(10^6\)输出\(-1\)就行了 不过我并不会证上界,据说是因为如果\(a=b=c\)且都是偶数肯定\(-1\),否则设\(a\le ...
随机推荐
- 算法马拉松35 E 数论只会Gcd - 类欧几里得 - Stern-Brocot Tree - 莫比乌斯反演
题目传送门 传送门 这个官方题解除了讲了个结论,感觉啥都没说,不知道是因为我太菜了,还是因为它真的啥都没说. 如果 $x \geqslant y$,显然 gcd(x, y) 只会被调用一次. 否则考虑 ...
- netcore与ef资料收集
http://www.cnblogs.com/cgzl/p/7661805.html https://www.cnblogs.com/cgzl/p/7675485.html https://www.c ...
- [BAT脚本] 1、BAT脚本FOR循环操作文件和命令返回实例
Wednesday, 31. October 2018 08:18PM - beautifulzzzz 一.需求 需要在windows上实现一个bat脚本解析json,将json转换为自己想要的key ...
- Docker安装和上传容器
安装Docker Requirements for Mac(硬件要求): Mac的硬件必须是2010或者更新的,需要支持memory management unit(MMU) virtualizait ...
- ScheduledThreadPoolExecutor周期任务或延时任务线程池
ScheduledThreadPoolExecutor可以代替timer,timer的缺点是一个timer启动一个线程,如果任务数量很多,会创建很多线程,不推荐使用. ScheduledThreadP ...
- 哪个版本的gcc才支持c11
而知,低版本的gcc不支持c11. (而我此处的eglibc 2.17,和那人的glibc-2.16.0,都是需要支持c11的gcc的) 所以此处想要去搞清楚,什么版本的,哪个版本的,gcc,才支持c ...
- __attribute__((format(printf, a, b)))
最近,在看libevent源码,第一次看到__attribute__((format(printf, a, b)))这种写法.因此,在这里记录下用法. 功能:__attribute__ format属 ...
- SFTP 定时任务下载
1.上传 winscp.exe /console /command "option batch continue" "option confirm off" & ...
- 关于toLocaleString(), toString(), valueOf()方法的使用
所有对象都是具有toLocalString(), toString(), valueOf()三种方法的,此篇博客主要是讲述其在Array引用类型上的使用. 基本使用 调用valueOf()返回的是数组 ...
- Git 版本及版本范围表示法
很多 Git 命令都使用 revision(修订版本)作为参数.根据不同的命令,有时候 revision 参 数代表一个特定的提交,有时候代表某一个提交可以追踪到的所有的父提交(比如 git log) ...