• 【日常摸鱼】牛客挑战赛2


    【日常摸鱼】牛客挑战赛2

    前言

    略了。。不多bb

    A Cut

    等同于合并果子,水题略了

    B Travel

    裸最短路,水题略了

    C Butterfly

    链接

    https://ac.nowcoder.com/acm/contest/17/C

    题意

    给定一个n*m的矩阵,矩阵元素由X和O构成,请求出其中最大的由X构成的蝴蝶形状。
    由X构成的蝴蝶形状的定义如下:
    存在一个中心点,并且其往左上、左下、右上、右下四个方向扩展相同的长度(扩展的长度上都是X),且左上顶点与左下顶点、右上顶点与右下顶点之间的格子全由X填充。我们不在意在蝴蝶形状内部是X还是O。
    例如:
    XOOOX
    XXOXX
    XOXOX
    XXOXX
    XOOOX
    是一个X构成的蝴蝶形状。
    X也是。

    XOOX
    OXXO
    OXXO
    XOXX
    不是(不存在中心点)。
    (n,mleq 2000)

    题解

    因为蝴蝶形中间可以有空的,不好二分。。我们先想办法转化成能二分的东西。。
    一个蝴蝶形可以看做两个(一竖+一斜杠),我们先算出从一个点(i,j)最大的能向左或向右的(竖+斜)。
    这个是连续的,因为能有长度为x的(竖+斜),就能有长度为x-1的。
    然后一个蝴蝶形就是两个同一行的(竖+斜)的组合。
    设R(i,j)和L(i,j)分别表示(i,j)可以向左或向右延伸的最大的(竖+斜)。
    然后假设我们能用(i,j)和(i,k)组合出一个蝴蝶形。
    于是有:
    (R(i,j)geq k-j+1)
    (L(i,k)geq k-j+1)
    移项得:
    (R(i,j)+j-1 geq k geq j)
    (j geq k+1-L(i,k))
    对于同一行,按照这两个不等式,就能套用各种数据结构了。。我用的是set。。

    (Code)

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    const LL INF=1e18;
    const int N=2e5+100;
    int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    void print(LL x){
        if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    void get_min(LL &x,LL y){
        if(x>y) x=y;
    }
    int n,m; 
    char s[2010];
    int mp[2010][2010];
    int d[2010][2010],dr[2010][2010],dl[2010][2010];
    int L[2010][2010],R[2010][2010];
    set<int> S[2];
    set<int>::iterator it;
    int id[2010],val[2010];
    bool cmp(int x,int y){
        return val[x]<val[y];
    }
    int main(){
        int x,ans=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i){
            scanf("%s",s+1);
            for(int j=1;j<=m;++j){
                if(s[j]=='X') mp[i][j]=1;
                else mp[i][j]=0;
            }
        }
        for(int i=n;i>=1;--i){
            for(int j=1;j<=m;++j){
                if(!mp[i][j]) d[i][j]=dr[i][j]=dl[i][j]=0;
                else{
                    d[i][j]=dr[i][j]=dl[i][j]=1;
                    if(i<n) d[i][j]+=d[i+1][j];
                    if(i<n&&j<m) dr[i][j]+=dr[i+1][j+1];
                    if(i<n&&j>1) dl[i][j]+=dl[i+1][j-1];
                }
            }
        }
        for(int i=n;i>=1;--i){
            for(int j=1;j<=m;++j){
                L[i][j]=min(d[i][j],dr[i][j]);
                R[i][j]=min(d[i][j],dl[i][j]);
            }
        }
        for(int i=1;i<=m;++i) id[i]=i;
        for(int i=1;i<=n;++i){
            S[0].clear();S[1].clear();
            int k;
            for(k=1;k<=m;++k){
                val[k]=k+1-R[i][k];
            }
            sort(id+1,id+1+m,cmp);
            S[0].insert(0);S[1].insert(0);k=1;
            for(int j=1;j<=m;++j){
                while(k<=m&&val[id[k]]<=j){
                    S[(id[k]&1)].insert(id[k]);
                    ++k;
                }
                it=S[(j&1)].upper_bound(L[i][j]+j-1);
                --it;
                x=*it;
                if(x>=j&&x<=L[i][j]+j-1){
                    ans=max(ans,x-j+1);
                }
            }
        }
        printf("%d
    ",ans);
        return 0;
    }
    

    D Delete

    链接

    https://ac.nowcoder.com/acm/contest/17/D

    题意

    给定一张n个点,m条边的带权有向无环图,同时给定起点S和终点T,一共有q个询问,每次询问删掉某个点和所有与它相连的边之后S到T的最短路,询问之间互相独立(即删除操作在询问结束之后会立即撤销),如果删了那个点后不存在S到T的最短路,则输出-1。
    (n,qleq 100000,mleq 200000)

    题解

    看到是DAG上求距离,先拓扑排序一波。
    排好之后,ST间的每条路径都相当于在拓扑序上按顺序选几个点连起来。
    我们先预处理S到所有点的距离和所有点到T的距离。
    如果删的是拓扑序在S前或在T后的,直接输出ST的最短距离。
    对于其他点,考虑一条路径对答案的影响,相当于对这条路径没经过的所有点取个min。
    这样的话我们枚举每一条边(x,y),对拓扑序在xy之间的所有点用dis(S,x)+len(x,y)+dis(y,T)来取min。
    这样的话问题就是区间min和单点查询了,可以用线段树。

    (Code)

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    const LL INF=1e18;
    const int N=2e5+100;
    int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    void print(LL x){
        if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    int n,m,S,T;
    struct edge{int x,y;LL z;}e[N];
    vector<int> L[N],R[N];
    int du[N];
    int q[N];
    int num[N];
    LL ds[N],dt[N];
    LL mn[N<<1];
    void upd(int l,int r,int id,LL w,int lq,int rq){
        if(lq==l&&rq==r){
            mn[id]=min(mn[id],w);
            return;
        }
        int mid=(l+r)>>1;
        if(lq>mid) upd(mid+1,r,id<<1|1,w,lq,rq);
        else if(rq<=mid) upd(l,mid,id<<1,w,lq,rq);
        else{
            upd(l,mid,id<<1,w,lq,mid);
            upd(mid+1,r,id<<1|1,w,mid+1,rq);
        }
        return;
    }
    LL ask(int l,int r,int id,int x){
        if(l==r) return mn[id];
        int mid=(l+r)>>1;
        if(x<=mid) return min(ask(l,mid,id<<1,x),mn[id]);
        else return min(ask(mid+1,r,id<<1|1,x),mn[id]);
    }
    int main(){
        n=read();m=read();S=read();T=read();
        for(int i=1;i<=m;++i){
            e[i].x=read();e[i].y=read();e[i].z=read();
            ++du[e[i].y];
            R[e[i].x].push_back(i);
            L[e[i].y].push_back(i);
        }
        int l=1,r=0,x,y;edge ed;LL ans;
        for(int i=1;i<=n;++i) if(!du[i]) q[++r]=i;
        while(l<=r){
            x=q[l++];
            for(int i=0;i<R[x].size();++i){
                y=e[R[x][i]].y;
                --du[y];
                if(!du[y]) q[++r]=y;
            }
        }
        for(int i=1;i<=n;++i){
            num[q[i]]=i;
        }
        for(int i=1;i<=n;++i){
            ds[i]=dt[i]=INF;
        }
        ds[S]=0;
        for(int i=1;i<=r;++i){
            for(int j=0;j<R[q[i]].size();++j){
                ed=e[R[q[i]][j]];
                ds[ed.y]=min(ds[ed.y],ds[ed.x]+ed.z);
            }
        }
        dt[T]=0;
        for(int i=r;i>=1;--i){
            for(int j=0;j<L[q[i]].size();++j){
                ed=e[L[q[i]][j]];
                dt[ed.x]=min(dt[ed.x],dt[ed.y]+ed.z);
            }
        }
        for(int i=1;i<(N<<1);++i) mn[i]=INF;
        
        for(int i=1;i<=m;++i){
            ans=ds[e[i].x]+e[i].z+dt[e[i].y];
            if(ans<INF&&num[e[i].x]+1<=num[e[i].y]-1) 
                upd(1,n,1,ans,num[e[i].x]+1,num[e[i].y]-1);
        }
        int Q=read();
        while(Q--){
            x=read();
            if(num[x]<num[S]||num[x]>num[T]){
                if(ds[T]==INF)puts("-1");
                else printf("%lld
    ",ds[T]);
                continue;
            }
            ans=ask(1,n,1,num[x]);
            if(ans==INF) puts("-1");
            else printf("%lld
    ",ans);
        }
        return 0;
    }
    

    E Mod

    这个窝不会。。。
    题解大概是类似辗转相减的方法。。
    细节很多很不好写。。我就放弃了

  • 相关阅读:
    Oracle专家高级编程 第二章 服务器和文件
    Oracle专家高级编程 第一章
    Leetcode 4 寻找两个正序数组的中位数
    Leetcode 3 无重复字符的最长子串
    Leetcode 2 两数相加
    Leetcode 1 两数之和
    gitee开源许可证
    js新特性展开符的使用方式
    webpack高速配置
    JS中日期比较时斜杠与横杠的区别
  • 原文地址:https://www.cnblogs.com/Yuigahama/p/14377010.html
Copyright © 2020-2023  润新知