欢迎来到蒟蒻mqd的博客

Codeforces Round #619 (Div. 2)

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的最长边长。我是用左上的正方形的右下角当中间点。

定以:

  1. \(c[x][y]\) 以(x,y)坐标表示左上角的logo的边长
  2. \(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)\)

这个方法简单暴力,但是速度也不慢,因为常数很小,因为\(n\le 500\)所以其实\(n和log^2n\)差不多。

定义:

  1. \(cnt[k][x][y]\) 矩阵\((1,1)~(x,y)\)的颜色\(k(k<4)\)的个数。
  2. \(ans[l][x][y]\) 以(x,y)为左上角的logo的边长为\(2*l\)是否存在,存在为1
  3. \(sans[l][x][y]\) \(ans[l][x][y]\)的二维前缀和=\(\sum_{i=1}^x\sum_{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\n",ans);
        }
    }

F. Super Jaber

题解:

关键就是颜色只有40种,我们先考虑如果不通过相同颜色传送,那么虽短举例就是两点的曼哈顿举例,也就是\(|(r2-r1)|+|(c2-c1)|\)

那么我们假设至少用一次传送,我们就可以枚举哪个颜色一定会用传送。我们可以预处理出来每个点到某种颜色的最短距离。

定义:

  • \(dist[k][i][j]:(i,j)\rightarrow颜色k 的最短路径(可以传送)\)

那么枚举\(k\)\((r1,c1)\rightarrow(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;
        }
    }
posted @ 2020-03-04 09:23  mmqqdd  阅读(207)  评论(0编辑  收藏  举报