A. Three Strings
题意:给三个长度相同的非空字符串abc,依次将c中的每个字符和a或者b中对应位置的字符进行交换,交换必须进行,问能否使得ab相同。
思路:对于每一个位置,如果三个字符都不相同,那一定不同,如果有两个相同且不是ab相同,则合法,否则不合法,如果三个字符都相同,那么合法。
#include<bits/stdc++.h>
#define LL long long
#define dl double
void rd(int &x){
x=;int f=;char ch=getchar();
while(ch<'' || ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch<='' && ch>='')x=x*+ch-'',ch=getchar();x*=f;
}
void lrd(LL &x){
x=;int f=;char ch=getchar();
while(ch<'' || ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch<='' && ch>='')x=x*+ch-'',ch=getchar();x*=f;
}
const int INF=1e9;
const LL LINF=1e18;
const int inf=;
using namespace std;
int T;
char a[inf],b[inf],c[inf];
bool check(int x){
if(a[x] == b[x])return a[x] != c[x];
if(a[x] == c[x] || b[x] == c[x])return ;
return ;
}
int main(){
// freopen("in.txt","r",stdin);
rd(T);
while(T--){
scanf("%s%s%s",a,b,c);
int n=strlen(a),flg=;
for(int i=;i<n;i++)
if(check(i))flg=;
if(flg)printf("NO\n");
else printf("YES\n");
}
return ;
}
/**/
B. Motarack's Birthday
题意:给一串数,一些位置没有数字,选择一个数填到所有空位上,使得相邻两数之差的绝对值的最大值最小。
思路:找出旁边有空位的数字中最大的和最小的,相加除二便可以得到使得空位和非空位之间差的绝对值的最大值最小的数,再统计一下非空位之间的贡献即可。
#include<bits/stdc++.h>
#define LL long long
#define dl double
void rd(int &x){
x=;int f=;char ch=getchar();
while(ch<'' || ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch<='' && ch>='')x=x*+ch-'',ch=getchar();x*=f;
}
void lrd(LL &x){
x=;int f=;char ch=getchar();
while(ch<'' || ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch<='' && ch>='')x=x*+ch-'',ch=getchar();x*=f;
}
const int INF=1e9;
const LL LINF=1e18;
const int inf=1e5+;
using namespace std;
int T,n;
int a[inf];
int mx,mn;
int main(){
/// freopen("in.txt","r",stdin);
rd(T);
while(T--){
rd(n);mx=;mn=1e9+;
for(int i=;i<=n;i++)rd(a[i]);
for(int i=;i<n;i++){
if(a[i] == -)continue;
if(a[i-] == - || a[i+] == -)mx=max(mx,a[i]),mn=min(mn,a[i]);
}
if(a[] != - && a[] == -)mx=max(mx,a[]),mn=min(mn,a[]);
if(a[n] != - && a[n-] == -)mx=max(mx,a[n]),mn=min(mn,a[n]);
int ans1,ans2;ans2=(mn+mx)/;
if(mx)ans1=max(mx-ans2,ans2-mn);else ans1=;
for(int i=;i<n;i++)if(a[i]!=- && a[i+]!=-)ans1=max(ans1,abs(a[i+]-a[i]));
printf("%d %d\n",ans1,ans2);
}
return ;
}
/**/
C. Ayoub's function
题意:定义f(s)表示01串s中包含至少一个1的连续子串的数目,求长度为n且有m个1的01串中f(s)的最大值。
思路:一共有n*(n-1)/2个子串,我们只需要求出一个1都不包括的子串的数目的最小值即可得到答案。可以发现只需要对连续的0串进行统计即可,答案为(l-1)*l/2,容易发现使得0串长度尽可能平均会使得答案最小,于是将其分为相等的m+1组,多出来的再一个一个分配给每一组,这样就得到了所有0串的长度,统计答案即可。
#include<bits/stdc++.h>
#define LL long long
#define dl double
void rd(int &x){
x=;int f=;char ch=getchar();
while(ch<'' || ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch<='' && ch>='')x=x*+ch-'',ch=getchar();x*=f;
}
void lrd(LL &x){
x=;int f=;char ch=getchar();
while(ch<'' || ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch<='' && ch>='')x=x*+ch-'',ch=getchar();x*=f;
}
const int INF=1e9;
const LL LINF=1e18;
using namespace std;
int T;
int n,m;
int main(){
// freopen("in.txt","r",stdin);
rd(T);
while(T--){
rd(n),rd(m);
LL ans=;
if(n == m)ans=1ll*n*(n+)/;
else if(!m)ans = ;
else {
int u=(n-m)/(m+),v=(n-m)%(m+);
ans+=1ll*(m+-v)*u*(u+)/;
ans+=1ll*v*(u+)*(u+)/;
ans=1ll*n*(n+)/-ans;
}
printf("%lld\n",ans);
}
return ;
}
/**/
D. Time to Run
题意:nm的格子,相邻格子之间有两条相反的单向路,从11开始沿着路走,可以在任意一个格子结束,每条路只能走一次,问能否走k条路,并输出方案。
思路:可以发现应该存在一个可以走的路的最大值,大胆猜测存在一种方式可以一次走完所有的路,构造可以发现,一直往右,然后下,一直往左,然后下,直到最后一行,然后上下左(右)重复到边界,往上走,继续重复即可一次走完所有的路。需要注意输出有要求,需要对一些步骤进行压缩,可以证明压缩后一定在3000以内。还需要注意处理单列的情况。
#include<bits/stdc++.h>
#define LL long long
#define dl double
void rd(int &x){
x=;int f=;char ch=getchar();
while(ch<'' || ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch<='' && ch>='')x=x*+ch-'',ch=getchar();x*=f;
}
void lrd(LL &x){
x=;int f=;char ch=getchar();
while(ch<'' || ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch<='' && ch>='')x=x*+ch-'',ch=getchar();x*=f;
}
const int INF=1e9;
const LL LINF=1e18;
const int inf=;
using namespace std;
int n,m,k;
struct ghb{
int x;
char s[];
}sta[inf];
int top;
int main(){
// freopen("in.txt","r",stdin);
// freopen("a.out","w",stdout);
rd(n);rd(m);rd(k);
if(*n*m-*n-*m < k)printf("NO\n");
else {
printf("YES\n");
for(int i=;i<=n;i++){
if(!k)break;
if(i & ){
if(k <= m-){sta[++top]=(ghb){k,"R"};k=;break;}
k-=m-;if(m-)sta[++top]=(ghb){m-,"R"};
}
else {
if(k <= m-){sta[++top]=(ghb){k,"L"};k=;break;}
k-=m-;if(m-)sta[++top]=(ghb){m-,"L"};
}
if(k && i != n)sta[++top]=(ghb){,"D"},k--;
}
for(int i=n;i>;i--){
if(!k)break;
if(i & ){
if(k <= (m-)*){
int tmp1=k/,tmp2=k%;
if(tmp1)sta[++top]=(ghb){tmp1,"UDL"};
if(tmp2 == )sta[++top]=(ghb){,"U"};
if(tmp2 == )sta[++top]=(ghb){,"UD"};
k=;break;
}
if(m-)sta[++top]=(ghb){m-,"UDL"};k-=(m-)*;
}
else {
if(k <= (m-)*){
int tmp1=k/,tmp2=k%;
if(tmp1)sta[++top]=(ghb){tmp1,"UDR"};
if(tmp2 == )sta[++top]=(ghb){,"U"};
if(tmp2 == )sta[++top]=(ghb){,"UD"};
k=;break;
}
if(m-)sta[++top]=(ghb){m-,"UDR"};k-=(m-)*;
}
if(k)k--,sta[++top]=(ghb){,"U"};
}
if(k)sta[++top]=(ghb){k,"L"};
printf("%d\n",top);
for(int i=;i<=top;i++)printf("%d %s\n",sta[i].x,sta[i].s);
}
return ;
}
/**/
反思:遇到这种明显存在一个分界点的题,应该大胆猜想分界点是那个最特殊的点,nm格子这种题要单独考虑单行单列的情况。
E. Nanosoft
题意:定义一个logo是一个正方形,左上部分是红色,右上部分是绿色,左下部分是黄色,右下部分是蓝色,给一个nm的图,q次询问每次询问一个矩形区间内最大的logo有多大。
思路:首先可以考虑找出所有logo的位置,可以枚举中心,一层一层往外"剥",这样可以在nm时间复杂度内预处理出来,并可以得到他们的大小。考虑每次询问,试想我们从外往里一层一层剥,记录剥掉的层数k,再求出内部所有logo中心位置的logo大小的最大值,二者取较小值便可以得到一个小于等于答案的数,当内部的答案对应logo中心在最外层时便可以使得求出的值等于答案(当然其他的k也可能对应答案),区间最大值用二维st表解决即可,需要注意有很多边界的细节问题,此时时间复杂度已经合适,但有更优的解法,因为我们可以发现内部最大值随层数k的增加是递减的,同时得到最终答案的时候层数k一定是小于等于内部最大值的,所以应该找到尽可能大的k,所以可以二分答案找到满足k小于等于内部最大值的最大的k。需要注意上述说明中的小于或是大于之类的字眼都可能存在+1-1再比较大小的情况,因为我的代码中是取中心的左上角作为真正的中心,导致后面很多地方都需要+1-1之类的细节,具体情况画图即可得知。
#include<bits/stdc++.h>
#define LL long long
#define dl double
void rd(int &x){
x=;int f=;char ch=getchar();
while(ch<'' || ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch<='' && ch>='')x=x*+ch-'',ch=getchar();x*=f;
}
void lrd(LL &x){
x=;int f=;char ch=getchar();
while(ch<'' || ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch<='' && ch>='')x=x*+ch-'',ch=getchar();x*=f;
}
const int INF=1e9;
const LL LINF=1e18;
const int N=;
using namespace std;
int n,m,q;
char s[N][N];
int a[N][N];
int work(int x,int y,int z){
for(int i=;i<z;i++)if(s[x][y+i] != 'R' || s[x+*z-][y+i] != 'Y' || s[x+i][y] != 'R' || s[x+i][y+*z-] != 'G')return z-;
for(int i=z;i<*z;i++)if(s[x][y+i] != 'G' || s[x+*z-][y+i] != 'B' || s[x+i][y] != 'Y' || s[x+i][y+*z-] != 'B')return z-;
if(x > && y > && x+*z <= n && y+*z <= m)return work(x-,y-,z+);else return z;
}
int st[N][N][][];
int f[N];
int get_ans(int x1,int y1,int x2,int y2){
if(x1 > x2 || y1 > y2)return ;
int hx=f[x2-x1+],hy=f[y2-y1+];
return max(max(st[x1][y1][hx][hy],st[x1][y2-(<<hy)+][hx][hy]),max(st[x2-(<<hx)+][y1][hx][hy],st[x2-(<<hx)+][y2-(<<hy)+][hx][hy]));
}
int main(){
// freopen("in.txt","r",stdin);
rd(n);rd(m);rd(q);
for(int i=;i<=n;i++)scanf("%s",s[i]+);
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
a[i][j]=work(i,j,);
f[]=-;for(int i=;i<=max(n,m);i++)f[i]=f[i>>]+;
for(int i=;(<<i) <= n;i++)
for(int j=;(<<j) <= m;j++)
for(int x=;x+(<<i)- <= n;x++)
for(int y=;y+(<<j)- <= m;y++){
if(!i && !j)st[x][y][i][j]=a[x][y];
else if(!i)st[x][y][i][j]=max(st[x][y][i][j-],st[x][y+(<<j-)][i][j-]);
else st[x][y][i][j]=max(st[x][y][i-][j],st[x+(<<i-)][y][i-][j]);
}
for(int i=;i<=q;i++){
int x1,y1,x2,y2;rd(x1);rd(y1);rd(x2);rd(y2);x2--;y2--;
if(x2 < x1 || y2 < y1){printf("0\n");continue;}
int l=,r=min(x2-x1+,y2-y1+)/;
while(l != r){
int mid=(l+r>>)+;
if(get_ans(x1+mid,y1+mid,x2-mid,y2-mid) >= mid+)l=mid;
else r=mid-;
}
int tmp=min(get_ans(x1+l,y1+l,x2-l,y2-l),l+);
printf("%d\n",*tmp*tmp);
}
return ;
}
/**/
反思:一种解题的新思路吧,通过加上一种限定来使得当前答案可求,而这种限定甚至可以用二分优化。
F. Super Jaber
题意:nm格子,每个格子有颜色,颜色最多40种,每一步可以上下左右或者从当前格子跳到任意一个颜色相同的其他格子,q次询问两点之间的最短距离。
思路:如果不同色跳,那么横纵坐标差的绝对值加和即为答案,如果进行同色跳,不妨枚举用其中一次是哪一种同色跳,这就需要我们处理出每种颜色离一个点最近距离是多少,按颜色bfs,将所有该颜色点一次推入,用两种方式转移,需要注意同色跳操作在对于每种颜色预处理时每一种颜色至多一次(需要直接特判掉不然枚举的复杂度会很高)。
#include<bits/stdc++.h>
#define LL long long
#define dl double
void rd(int &x){
x=;int f=;char ch=getchar();
while(ch<'' || ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch<='' && ch>='')x=x*+ch-'',ch=getchar();x*=f;
}
void lrd(LL &x){
x=;int f=;char ch=getchar();
while(ch<'' || ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch<='' && ch>='')x=x*+ch-'',ch=getchar();x*=f;
}
const int INF=1e9;
const LL LINF=1e18;
const int N=;
const int K=;
using namespace std;
int n,m,k,q;
int a[N][N];
int dis[N][N][K];
struct Point{
int x,y;
};
queue<Point>S;
int dx[]={,-,,},dy[]={,,,-};
vector<Point>T[K];
bool tag[K];
void get_dis(int x){
for(int i=;i<T[x].size();i++)S.push(T[x][i]),dis[T[x][i].x][T[x][i].y][x]=;
memset(tag,,sizeof(tag));
while(!S.empty()){
Point u=S.front();S.pop();
for(int i=;i<;i++){
int tx=dx[i]+u.x,ty=dy[i]+u.y;
if(tx < || tx > n || ty < || ty > m || dis[tx][ty][x] != 0x3f3f3f3f)continue;
dis[tx][ty][x]=dis[u.x][u.y][x]+;S.push((Point){tx,ty});
}
int col=a[u.x][u.y];
if(!tag[col]){
tag[col]=;
for(int i=;i<T[col].size();i++)
if(dis[T[col][i].x][T[col][i].y][x] == 0x3f3f3f3f)
dis[T[col][i].x][T[col][i].y][x]=dis[u.x][u.y][x]+,S.push(T[col][i]);
}
}
}
int main(){
// freopen("in.txt","r",stdin);
rd(n);rd(m);rd(k);
for(int i=;i<=n;i++)for(int j=;j<=m;j++)rd(a[i][j]),T[a[i][j]].push_back((Point){i,j});
memset(dis,0x3f,sizeof(dis));for(int i=;i<=k;i++)get_dis(i);rd(q);
for(int i=;i<=q;i++){
int r1,c1,r2,c2;rd(r1);rd(c1);rd(r2);rd(c2);
int ans=abs(r1-r2)+abs(c1-c2);
for(int j=;j<=k;j++)ans=min(ans,dis[r1][c1][j]+dis[r2][c2][j]+);
printf("%d\n",ans);
}
return ;
}
/**/
反思:这使我回忆起了一个很常见好用的套路,即预处理部分,"求出距离当前点最近被标记点",这种可以通过一次性将所有被标记的点推入队列进行bfs得到。