Codeforces Round #619 (Div. 2)
题目链接:https://codeforces.com/contest/1301
A题跳过
B题
这题一开始跳了,后来回来用二分做的。二分答案,然后查询是否满足,查询用一个区间表示现在k 可以取值的范围。如果左端点比右端点大了,说明没有可能取值。但是,后来看了题解,发现直接取所有-1,两边的数的((最 大 值+最 小 值)/2)即为k。还是太菜了。
代码
#include<bits/stdc++.h>
using namespace std;
using vi=vector<int>;
using pii=pair<int,int>;
#define ll long long
const int INF=1e9+7;
const int N=500050;
int a[N],n;
int check(int x){
int l=0,r=INF;
for(int i=2;i<=n;i++){
if (a[i]==-1){
if (a[i-1]==-1) continue;
l=max(l,a[i-1]-x);
r=min(r,a[i-1]+x);
}else if (a[i-1]==-1){
l=max(l,a[i]-x);
r=min(r,a[i]+x);
}else{
if (abs(a[i]-a[i-1])>x)return -1;
}
if (l>r)return -1;
}
return l;
}
int ef(int l,int r){
if (l==r)return l;
int mid=(l+r)>>1;
if (check(mid)==-1)return ef(mid+1,r);
else return ef(l,mid);
}
void work(){
int num=0;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
int m=ef(0,INF);
int k=check(m);
cout<<m<<" "<<k<<endl;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("aa.in","r",stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin>>T;
while(T--)work();
}
C. Ayoub's function
贪心,肯定在0中间放1,连续的0的长度越短越好,就平均放1。然后算答案。
至于为什么,假如连续的0的个数为(l),全为0的子串就有(l*(l+1)/2), 列出前面几项(1,3,6,10,15,21) 越往后,增加一个0就会增加更多的子串,所以要使得平均越短越好。
代码
#include<bits/stdc++.h>
using namespace std;
using vi=vector<int>;
using pii=pair<int,int>;
#define ll long long
const int INF=0x7f7f7f7f;
const int N=2000050;
void work(){
ll n,m,x,y;
cin>>n>>m;
if (m>=n-m+1){
cout<<(n*(n+1)/2)-(n-m)<<endl;
return;
}
x=(n-m)/(m+1);
y=(n-m)%(m+1);
ll ans=x*(x+1)/2*(m+1-y)+(x+1)*(x+2)/2*y;
ans=(n*(n+1))/2-ans;
cout<<ans<<endl;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("aa.in","r",stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin>>T;
while(T--)work();
}
D. Time to Run
题解
先找一种方法,可以走完所有的边。我是先一直往右走,走到底(1,m),再走回(1,1)。
之后就先往下走一格,再“RUL”不断重复往右边走,走到底再往左直走回来。
如此循环,直到最后停在(n,1) ,这时再往上走回(1,1).
知道怎么走之后,关键就是怎么输出了。我可真是不太行,于是就去看大佬的代码,发现太巧妙了。就是先将这些步骤全部以pair打入一个vector中,first存重复多少次,second存怎么走。每个pair就相当于一个二维数组一样。比如元素a,
a.first*b.size()就是这个二维数组的大小。然后就一个一个块扫,如果大于整个快,就直接算答案,如果小于快,就在内部处理。总之就是很方便,代码很短很容易理解。
代码
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef long long ll;
const int INF=1e9+7;
const int N=500050;
vector<pair<int,string>> ans,pans;
void work(){
int n,m,k;
cin>>n>>m>>k;
if (k>4*n*m-2*n-2*m){
puts("NO");
return;
}
ans.push_back({m-1,"R"});
ans.push_back({m-1,"L"});
for(int i=1;i<=n-1;i++){
ans.push_back({1,"D"});
ans.push_back({m-1,"RUD"});
ans.push_back({m-1,"L"});
}
ans.push_back({n-1,"U"});
for(auto x:ans){
if (x.fi==0)continue;//这里主要是防止m=1的情况
if (x.fi*x.se.size()<=k){
pans.push_back(x);
k-=x.fi*x.se.size();
}else{
if (k/x.se.size())pans.push_back({k/x.se.size(),x.se});
if (k%x.se.size())pans.push_back({1,x.se.substr(0,k%x.se.size())});
k=0;
}
}
cout<<"YES"<<endl;
cout<<pans.size()<<endl;
for(auto x:pans)cout<<x.fi<<" "<<x.se<<endl;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("aa.in","r",stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
work();
}
E. Nanosoft
题解
解法一 (O(n^2log^2n + qlogn))
这个分成两个部分,也是std的做法。
第一步,先求每个点,为中间点,往外的logo的最长边长。我是用左上的正方形的右下角当中间点。
定以:
- (c[x][y]) 以(x,y)坐标表示左上角的logo的边长
- (b[x][y]) 以(x,y)坐标为中间点的logo的最大边长,也就是红色正方形的右下角那个点。
先求(c[x][y]。)
- 当最外面一圈合法时:(c[x][y]=c[x+1][y+1]+2)
- 当最外面一圈不合法:(c[x][y]=0)
扫描最外面一圈,我是直接暴力,我觉得这个是(O(n^2))因为除了每个logo的最外层,其他的点不会被扫到两遍。之后再用(c[x][y]求b[x][y]),这个很好求,扫描一遍(对于每一个c[x][y],b[x+c[x][y]/2-1][y+c[x][y]/2-1]=max(c[x][y],自己)).
第二步,用二分+sparse table求结果。
二分答案,然后用二维ST表求最大值是否大于答案,需要注意边界问题。假如我们现在枚举答案为len(实际边长为2*len),区间为(r1,c1)~(r2,c2)。则实际求最大值的范围为(r1+len-1,c1+len-1)~(r2-len,c2-len)。因为左上角和右下角的值都会越出边界。大概思路就是这样。我不知道为什么我的代码跑起来就是慢。
解法二 (O(n^3+qn))
这个方法简单暴力,但是速度也不慢,因为常数很小,因为(nle 500)所以其实(n和log^2n)差不多。
定义:
- (cnt[k][x][y]) 矩阵((1,1)~(x,y))的颜色(k(k<4))的个数。
- (ans[l][x][y]) 以(x,y)为左上角的logo的边长为(2*l)是否存在,存在为1
- (sans[l][x][y]) (ans[l][x][y])的二维前缀和=(sum_{i=1}^xsum_{j=1}^y ans[l][i][j])
这几个东西都很好求,就是二维前缀和。然后求答案,就每一种答案都判断所求举证里的二维前缀和是否大于零。边界问题还是要注意,这里不讲了。当然也可以二分。
代码一 ST+二分答案 (O(n^2log^2n))
#include<bits/stdc++.h>
using namespace std;
const int N = 505;
struct Square{int x1,y1,x2,y2;};
char mp[N][N];
int b[N][N],c[N][N];
struct ST{
int mx[N][N][10][10],mi[10],lg[N];
void build(int c[N][N],int n, int m){
lg[0]=-1;
for(int i=1;i<=max(n,m);i++)lg[i]=lg[i>>1]+1;
for(int i=0;i<10;i++)mi[i]=1<<i;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
mx[i][j][0][0]=c[i][j];
for(int k=1;k<10;k++)
for(int i=1;i<=n;i++)
for(int j=1;j+mi[k]-1<=m;j++){
mx[i][j][0][k]=max(mx[i][j][0][k-1],mx[i][j+mi[k-1]][0][k-1]);
}
int x,y;
for(int k1=1;k1<10;k1++)
for(int k2=0;k2<10;k2++)
for(int i=1;i+mi[k1]-1<=n;i++)
for(int j=1;j+mi[k2]-1<=m;j++){
x=max(mx[i][j][k1-1][k2],mx[i+mi[k1-1]][j][k1-1][k2]);
mx[i][j][k1][k2]=x;
}
}
int get(int x1, int y1, int x2, int y2){
if (x1>x2 || y1>y2)return 0;
int k1=lg[x2-x1+1], k2=lg[y2-y1+1],val1,val2;
x2-=mi[k1]-1; y2-=mi[k2]-1;
val1=max(mx[x1][y1][k1][k2],mx[x1][y2][k1][k2]);
val2=max(mx[x2][y1][k1][k2],mx[x2][y2][k1][k2]);
return max(val1,val2);
}
}St;
bool check(int x, int y, int len){
for(int i=0;i<len/2;i++){
if (mp[x][y+i]!='R') return 0;
if (mp[x][y+len/2+i]!='G') return 0;
if (mp[x+len-1][y+i]!='Y') return 0;
if (mp[x+len-1][y+len/2+i]!='B') return 0;
if (mp[x+i][y]!='R') return 0;
if (mp[x+len/2+i][y]!='Y') return 0;
if (mp[x+i][y+len-1]!='G') return 0;
if (mp[x+len/2+i][y+len-1]!='B')return 0;
}
return 1;
}
bool efcheck(Square a, int len ){
a.x1+=len-1; a.y1+=len-1;
a.x2-=len; a.y2-=len;
if (St.get(a.x1,a.y1,a.x2,a.y2)>=len)return 1;
return 0;
}
int ef(Square a,int l,int r){
if (l==r)return l;
int mid=(l+r+1)>>1;
if (efcheck(a,mid))return ef(a,mid,r);
else return ef(a,l,mid-1);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("aa.in","r",stdin);
#endif
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int n,m,k;
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
cin>>mp[i]+1;
for(int i=n;i>=1;i--)
for(int j=m;j>=1;j--){
int x=i+1, y=j+1;
if (x>n || y>m)continue;
if (check(i,j,c[i+1][j+1]+2))
c[i][j]=c[i+1][j+1]+2;
}
for(int i=1;i<n;i++)
for(int j=1;j<m;j++){
if (c[i][j]==0) continue;
int x=i+c[i][j]/2-1, y=j+c[i][j]/2-1;
b[x][y]=max(b[x][y],c[i][j]/2);
}
St.build(b,n,m);
//cout<<St.get(1,1,n,m)<<endl;
int x1,y1,x2,y2,ans;
for(int i=1;i<=k;i++){
cin>>x1>>y1>>x2>>y2;
Square a={x1,y1,x2,y2};
ans=ef(a,0,x2-x1+1);
ans*=ans*4;
cout<<ans<<endl;
}
}
代码二 暴力枚举答案 (O(n^3+q n))
#include<bits/stdc++.h>
using namespace std;
const int N=505;
string col="RGYB";
int n,m,q,cnt[255][N][N];
char grid[N][N];
int get(int k,int x1,int y1,int x2,int y2){
return cnt[k][x2][y2]-cnt[k][x1-1][y2]-cnt[k][x2][y1-1]+cnt[k][x1-1][y1-1];
}
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++) scanf("%s",grid[i]+1);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=0;k<4;k++)
if (grid[i][j]==col[k])cnt[k][i][j]=1;
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int k=0;k<4;k++)cnt[k][i][j]+=cnt[k][i-1][j];
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int k=0;k<4;k++)cnt[k][i][j]+=cnt[k][i][j-1];
for(int l=1;l<=250;l++){
if (2*l>n || 2*l>m) break;
for(int x=l;x<=n-l;x++)
for(int y=l;y<=m-l;y++){
if (get(0,x-l+1,y-l+1,x,y)!=l*l)continue;
if (get(1,x-l+1,y+1,x,y+l)!=l*l)continue;
if (get(2,x+1,y-l+1,x+l,y)!=l*l)continue;
if (get(3,x+1,y+1,x+l,y+l)!=l*l)continue;
cnt[l+3][x-l+1][y-l+1]=1;
}
for(int i=1;i<=n-2*l+1;i++)
for(int j=1;j<=m-2*l+1;j++)
cnt[l+3][i][j]+=cnt[l+3][i-1][j];
for(int i=1;i<=n-2*l+1;i++)
for(int j=1;j<=m-2*l+1;j++)
cnt[l+3][i][j]+=cnt[l+3][i][j-1];
}
int r1,c1,r2,c2,ans;
for(int i=1;i<=q;i++){
scanf("%d%d%d%d",&r1,&c1,&r2,&c2);
ans=0; r2++; c2++;
for(int l=1;l<=250;l++){
r2-=2; c2-=2;
if (r1>r2 || c1>c2)break;
if (get(l+3,r1,c1,r2,c2)>0)ans=l;
}
ans=ans*ans*4;
printf("%d
",ans);
}
}
F. Super Jaber
题解:
关键就是颜色只有40种,我们先考虑如果不通过相同颜色传送,那么虽短举例就是两点的曼哈顿举例,也就是(|(r2-r1)|+|(c2-c1)|)。
那么我们假设至少用一次传送,我们就可以枚举哪个颜色一定会用传送。我们可以预处理出来每个点到某种颜色的最短距离。
定义:
- (dist[k][i][j]:(i,j) ightarrow颜色k 的最短路径(可以传送))
那么枚举(k),((r1,c1) ightarrow(r2,c2)最短距离=min{ dist[k][r1][c1]+dist[k][r2][c2]+1})
求(dist)数组就用(bfs),每种颜色做一遍(bfs),注意一下扩展相同颜色就行了。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
typedef vector<pair<int,int>> vpii;
const int N=1050;
const int K=45;
int n,m,k;
int grid[N][N],dist[K][N][N];
int lx[4]={1,-1,0,0};
int ly[4]={0,0,-1,1};
deque<pii> color[K];
void bfs(int dist[N][N], deque<pii> Q){
static int f[K];
memset(f,0,sizeof(f));
while(!Q.empty()){
pii u=Q.front(); Q.pop_front();
int x=u.first, y=u.second;
if(f[grid[x][y]]==0){
f[grid[x][y]]=1;
auto it=color[grid[x][y]].begin();
for(;it!=color[grid[x][y]].end();it++){
int xx=it->first, yy=it->second;
if (dist[xx][yy]<=dist[x][y]+1)continue;
dist[xx][yy]=dist[x][y]+1;
Q.push_back({xx,yy});
}
}
for(int i=0;i<4;i++){
int xx=x+lx[i], yy=y+ly[i];
if (xx<1 || xx>n || yy<1 || yy>m)continue;
if (dist[x][y]+1>=dist[xx][yy])continue;
dist[xx][yy]=dist[x][y]+1;
Q.push_back({xx,yy});
}
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("aa.in","r",stdin);
#endif
scanf("%d%d%d",&n,&m,&k);
memset(dist,63,sizeof(dist));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
scanf("%d",grid[i]+j);
color[grid[i][j]].push_back({i,j});
dist[grid[i][j]][i][j]=0;
}
for(int i=1;i<=k;i++)bfs(dist[i],color[i]);
int q;
scanf("%d",&q);
while(q--){
int r1,c1,r2,c2;
scanf("%d%d%d%d",&r1,&c1,&r2,&c2);
int ans=abs(r1-r2)+abs(c2-c1);
for(int i=1;i<=k;i++)
ans=min(ans,dist[i][r1][c1]+dist[i][r2][c2]+1);
cout<<ans<<endl;
}
}