2019牛客暑期多校训练营(第八场)

目录

A 、All-one Matrices  (单调栈)

B 、Beauty Values   (计算贡献)

C 、CDMA  (递归)

D、 Distance (三维树状数组)

E 、Explorer  (线段树+并查集按秩合并)

G、Gemstones  (模拟)

J 、Just Jump  (DP+组合计数)


A 、All-one Matrices  (单调栈)

题意:

给出01矩阵,计算有多少个全1矩阵,并且每个全一矩阵必须是所能达到的最大的。

分析:

这类问题,一般解决就是两个方向:

    A:压缩一维,即枚举上下边界,然后复杂度O(N^3);   

    B:广告牌问题,即枚举下边界s,那么就可以看成s为底的一些高楼,再高楼上布置广告牌,复杂度O(N^2)。

A类问题  2019牛客第三场F

B类问题  HDU1506,2019牛客多校第二场H

这个题也是一个B类问题。

注意的有两点,1.这个区间只算一次(去重),2.这个区间底下全是1则不算。

#include<bits/stdc++.h>

#define mm(a,b) memset(a,b,sizeof(a))
#define ACCELERATE (ios::sync_with_stdio(false),cin.tie(0))
#define pii pair<int,int>
#define pid pair<double,double>
#define pil pair<long long,long long>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define rush() int T;scanf("%d",&T);while(T--)
#define sc(a) scanf("%d",&a)
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
#define PI acos(-1.0)
#define E exp(1.0)
//#define io
using namespace std;

const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;

//#define gc getchar
char buf[1<<21],*p1=buf,*p2=buf;
inline int gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
inline int read(){
    int ret=0,f=0;char c=gc();
    while(!isdigit(c)){if(c=='-')f=1;c=gc();}
    while(isdigit(c)){ret=ret*10+c-48;c=gc();}
    if(f)return -ret;return ret;
}

const int maxn=3e3+10;

int L[maxn],R[maxn];
int n,m;
char s[maxn][maxn];
int h[maxn][maxn];
int  vis[maxn][maxn];
int num[maxn];
int ans;

void solve(int t){
    rep(i,1,m) L[i]=R[i]=i;
    rep(i,2,m){
        int now=i;
        while(now>1&&h[t][i]<=h[t][now-1]) now=L[now-1];
        L[i]=now;
    }
    for(int i=m-1;i>=1;i--){
        int now=i;
        while(now<m&&h[t][i]<=h[t][now+1]) now=R[now+1];
        R[i]=now;
    }
    rep(i,1,m) num[i]=num[i-1]+(s[t+1][i]=='1');
    rep(i,1,m){
        if(h[t][i]==0) continue;
        if(vis[L[i]][R[i]]==t) continue;
        if(num[R[i]]-num[L[i]-1]==R[i]-L[i]+1) continue;
        ans++;
        vis[L[i]][R[i]]=t;
    }
}

int main(){
    #ifdef io
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif

    scanf("%d%d",&n,&m);
    rep(i,1,n){
        rep(j,1,m){
            scanf(" %c",&s[i][j]);
            if(s[i][j]=='1') h[i][j]=h[i-1][j]+1;
        }
    }
    rep(i,1,n) solve(i);
    printf("%d\n",ans);
    return 0;
}

B 、Beauty Values   (计算贡献)

题意:

计算所有区间不同数字的个数;

分析:

因为是计算所有区间所有可以直接算贡献,如果问的是任意一个区间,则可用主席树。

#include<bits/stdc++.h>

#define mm(a,b) memset(a,b,sizeof(a))
#define ACCELERATE (ios::sync_with_stdio(false),cin.tie(0))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
#define PI acos(-1.0)
#define E exp(1.0)
//#define io
using namespace std;

const int inf=0x3f3f3f3f;

const int maxn=1e5+10;

int a[maxn];
int pre[maxn];

int main(){
    #ifdef io
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif

    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    ll ans=0;
    for(int i=1;i<=n;i++){
        ans+=1ll*i*(n-i+1);
        ans-=1ll*pre[a[i]]*(n-i+1);
        pre[a[i]]=i;
    }
    printf("%lld\n",ans);
    return 0;
}

C 、CDMA  (递归)

题意:

给出一个m,计算m*m的矩阵使得每一行相乘结果都为零。

分析:

考虑由小矩阵构造大矩阵,所以我们可以这样构造,将小矩阵放在大矩阵的左上,右上,左下,右下均取反,那么构造出来的一个矩阵就是所求。

#include<bits/stdc++.h>

#define mm(a,b) memset(a,b,sizeof(a))
#define ACCELERATE (ios::sync_with_stdio(false),cin.tie(0))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
#define PI acos(-1.0)
#define E exp(1.0)
//#define io
using namespace std;

const int inf=0x3f3f3f3f;

const int maxn=1030;

int s[maxn][maxn];

void dfs(int x1,int y1,int x2,int y2,int dir,int dep){
    if(dep==1){
        s[x1][y1]=1*dir;
        s[x1][y1+1]=1*dir;
        s[x1+1][y1]=1*dir;
        s[x1+1][y1+1]=-1*dir;
        return;
    }
    int mid=1<<(dep-1);
    dfs(x1,y1,x1+mid-1,y1+mid-1,dir,dep-1);
    dfs(x1,y1+mid,x1+mid-1,y2,dir,dep-1);
    dfs(x1+mid,y1,x2,y1+mid-1,dir,dep-1);
    dfs(x1+mid,y1+mid,x2,y2,dir*-1,dep-1);
}

int main(){
    #ifdef io
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif

    int m;
    scanf("%d",&m);
    int i=0;
    for(;1<<i<m;i++);
    dfs(1,1,m,m,1,i);
    for(i=1;i<=m;i++){
        for(int j=1;j<=m;j++){
            printf("%d ",s[i][j]);
        }
        printf("\n");
    }
    return 0;
}

D、 Distance (三维树状数组)

题意:

两种操作:
1 x,y,h表示在x,y,h位置添加一个点
2 x,y,h表示询问与这个位置最近的点的曼哈顿距离是多少

分析:

将绝对值进行拆分,可以得到8种情况,所以将8种情况分别建立树状数组计算

我们就去八个树状数组中查询小于等于当前值的最大值,做差取最小值即可。

这么做为什么对呢,因为将拆分后的情况总能转化成这种形式。

哈希是为了保证所有的坐标都有不同的数字表示,

参考博客:https://blog.csdn.net/qq_41955236/article/details/99685766

#include<bits/stdc++.h>

#define mm(a,b) memset(a,b,sizeof(a))
#define ACCELERATE (ios::sync_with_stdio(false),cin.tie(0))
#define pii pair<int,int>
#define pid pair<double,double>
#define pil pair<long long,long long>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define rush() int T;scanf("%d",&T);while(T--)
#define sc(a) scanf("%d",&a)
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
#define PI acos(-1.0)
#define E exp(1.0)
//#define io
using namespace std;

const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;

//#define gc getchar
char buf[1<<21],*p1=buf,*p2=buf;
inline int gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
inline int read(){
    int ret=0,f=0;char c=gc();
    while(!isdigit(c)){if(c=='-')f=1;c=gc();}
    while(isdigit(c)){ret=ret*10+c-48;c=gc();}
    if(f)return -ret;return ret;
}

const int maxn=5e6+10;

int dx[]={1,1,1,1,-1,-1,-1,-1};
int dy[]={1,1,-1,-1,1,1,-1,-1};
int dz[]={1,-1,1,-1,1,-1,1,-1};

void deal(int i,int x,int y,int z,int &nx,int &ny,int &nz){
    nx=x*dx[i];
    ny=y*dy[i];
    nz=z*dz[i];
}

struct BIT{
    int mx[maxn],n,m,h;
    int g_hash(int x,int y,int z){
        return x*(2*m+2)*(2*h+2)+y*(2*h+2)+z;
    }
    void init(int nn,int mm,int hh){
        n=nn,m=mm,h=hh;
        for(int i=0;i<maxn;i++) mx[i]=-inf;
    }
    int lowbit(int x){
        return x&(-x);
    }
    void update(int x,int y,int z,int v){
        for(int i=x;i<2*n+5;i+=lowbit(i)){
            for(int j=y;j<2*m+5;j+=lowbit(j)){
                for(int k=z;k<2*h+5;k+=lowbit(k)){
                    mx[g_hash(i,j,k)]=max(mx[g_hash(i,j,k)],v);
                }
            }
        }
    }

    int query(int x,int y,int z){
        int ret=-inf;
        for(int i=x;i;i-=lowbit(i)){
            for(int j=y;j;j-=lowbit(j)){
                for(int k=z;k;k-=lowbit(k)){
                    ret=max(ret,mx[g_hash(i,j,k)]);
                }
            }
        }
        return ret;
    }
}B[8];

int main(){
    #ifdef io
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif

    int n,m,h,q;
    scanf("%d%d%d%d",&n,&m,&h,&q);
    for(int i=0;i<8;i++) B[i].init(n,m,h);
    while(q--){
        int op,x,y,z;
        scanf("%d%d%d%d",&op,&x,&y,&z);
        if(op==1){
            for(int i=0;i<8;i++){
                int nx,ny,nz;
                deal(i,x,y,z,nx,ny,nz);
                B[i].update(n+nx+1,m+ny+1,h+nz+1,nx+ny+nz);
            }
        }else{
            int ans=inf;
            for(int i=0;i<8;i++){
                int nx,ny,nz;
                deal(i,x,y,z,nx,ny,nz);
                ans=min(ans,nx+ny+nz-B[i].query(n+nx+1,m+ny+1,h+nz+1));
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}

E 、Explorer  (线段树+并查集按秩合并)

题意:

给定一个无向图,有n个点m条边,每条边有四个属性u,v,l,r,表示连接u,v的这条边允许通过的路人的体形范围在[l,r],问从1走到n的可行体形有几个

分析:

首先把区间离散化,然后建立一棵以区间为关键字的线段树,再把所有边都按区间加进对应的线段树节点。然后从线段树的根节点不断向下dfs,每次用按秩(即深度)合并优化并查集,不能用路径压缩,因为回溯的时候要撤销并查集操作。到了叶子结点的时候查询下1和n是否在一个集合里,是的话就加上这个区间的贡献,不是的话就继续向下dfs。
 

#include<bits/stdc++.h>

#define mm(a,b) memset(a,b,sizeof(a))
#define ACCELERATE (ios::sync_with_stdio(false),cin.tie(0))
#define pii pair<int,int>
#define pid pair<double,double>
#define pil pair<long long,long long>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define rush() int T;scanf("%d",&T);while(T--)
#define sc(a) scanf("%d",&a)
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
#define PI acos(-1.0)
#define E exp(1.0)
//#define io
using namespace std;

const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;

//#define gc getchar
char buf[1<<21],*p1=buf,*p2=buf;
inline int gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
inline int read(){
    int ret=0,f=0;char c=gc();
    while(!isdigit(c)){if(c=='-')f=1;c=gc();}
    while(isdigit(c)){ret=ret*10+c-48;c=gc();}
    if(f)return -ret;return ret;
}

const int maxn=2e5+10;

struct node{
    int u,v,l,r;
    node(){}
    node(int u,int v,int l,int r):u(u),v(v),l(l),r(r){}
}p[maxn];

int a[maxn<<1];
vector<pii> tree[maxn<<2];
pii V;
int n,m;

void build(int i,int l,int r,int x,int y){
    if(l==x&&y==r){
        tree[i].push_back(V);
        return;
    }
    int mid=l+r>>1;
    if(y<=mid) build(i<<1,l,mid,x,y);
    else if(x>mid) build(i<<1|1,mid+1,r,x,y);
    else build(i<<1,l,mid,x,mid),build(i<<1|1,mid+1,r,mid+1,y);
}

int fa[maxn<<1];
vector<int> t[100];
int rk[maxn<<1];
int ans;

int find(int x){
    return fa[x]==x?x:find(fa[x]);
}

void join(int x,int y,int dep){
    x=find(x),y=find(y);
    if(x==y) return;
    if(rk[x]<=rk[y]){
        fa[x]=y;
        t[dep].push_back(x);
        if(rk[x]==rk[y]) rk[y]++;
    }else{
        fa[y]=x;
        t[dep].pb(y);
    }
}

void dfs(int i,int l,int r,int dep){
    t[dep].clear();
    for(auto &tmp:tree[i]) join(tmp.fi,tmp.se,dep);
//    cout<<l<<" "<<r<<endl;
    if(l==r){
        if(find(1)==find(n)) ans+=a[r+1]-a[l];
        for(auto& tmp:t[dep]) fa[tmp]=tmp;
        return ;
    }
    int mid=l+r>>1;
    dfs(i<<1,l,mid,dep+1);
    dfs(i<<1|1,mid+1,r,dep+1);
    for(auto& tmp:t[dep]) fa[tmp]=tmp;
}

int main(){
    #ifdef io
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif

    for(int i=0;i<maxn*2;i++) fa[i]=i;
    scanf("%d%d",&n,&m);
    int cnt=0;
    rep(i,1,m){
        int u,v,l,r;
        scanf("%d%d%d%d",&u,&v,&l,&r);
        p[i]=node(u,v,l,r);
        a[++cnt]=l,a[++cnt]=r+1;
    }
    sort(a+1,a+1+cnt);
    cnt=unique(a+1,a+1+cnt)-a-1;
    a[cnt+1]=cnt+1;
    rep(i,1,m){
        V=mp(p[i].u,p[i].v);
        int l=lower_bound(a+1,a+1+cnt,p[i].l)-a;
        int r=lower_bound(a+1,a+1+cnt,p[i].r+1)-a;
        build(1,1,cnt,l,r-1);
    }
    ans=0;
    dfs(1,1,cnt,1);
    printf("%d\n",ans);
    return 0;
}

G、Gemstones  (模拟)

题意:

给出一个字符串每次消三个长度相同的,问消几次。

分析:

直接用栈模拟即可。

#include<bits/stdc++.h>

#define mm(a,b) memset(a,b,sizeof(a))
#define ACCELERATE (ios::sync_with_stdio(false),cin.tie(0))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
#define PI acos(-1.0)
#define E exp(1.0)
//#define io
using namespace std;

const int inf=0x3f3f3f3f;

int main(){
    #ifdef io
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif

    string s;
    cin>>s;
    vector<char> sta;
    if(s.size()<=2){
        cout<<"0"<<endl;
        return 0;
    }
    sta.push_back(s[0]);
    sta.push_back(s[1]);
    sta.push_back(s[2]);
    int ans=0;
    for(int i=3;i<s.size();i++){
        while(sta.size()>=3){
            bool flag=true;
            int len=sta.size();
            if(sta[len-1]==sta[len-2]&&sta[len-1]==sta[len-3]){
                ans++;
                sta.pop_back();
                sta.pop_back();
                sta.pop_back();
                flag=false;
            }
            if(flag) break;
        }
        sta.push_back(s[i]);
    }
    while(sta.size()>=3){
        bool flag=true;
        int len=sta.size();
        if(sta[len-1]==sta[len-2]&&sta[len-1]==sta[len-3]){
            ans++;
            sta.pop_back();
            sta.pop_back();
            sta.pop_back();
            flag=false;
        }
        if(flag) break;
    }
    cout<<ans<<endl;
    return 0;
}

J 、Just Jump  (DP+组合计数)

题意:

起点为0需要走到终点L,每次至少走d步,有m次攻击,ti时刻会出现在pi位置,也就是说在ti时刻不能在pi位置

分析:

参考的这篇博客:https://blog.csdn.net/ftx456789/article/details/99306117

#include<bits/stdc++.h>

#define mm(a,b) memset(a,b,sizeof(a))
#define ACCELERATE (ios::sync_with_stdio(false),cin.tie(0))
#define pii pair<int,int>
#define pid pair<double,double>
#define pil pair<long long,long long>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define rush() int T;scanf("%d",&T);while(T--)
#define sc(a) scanf("%d",&a)
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
#define PI acos(-1.0)
#define E exp(1.0)
//#define io
using namespace std;

const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;

//#define gc getchar
char buf[1<<21],*p1=buf,*p2=buf;
inline int gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
inline int read(){
    int ret=0,f=0;char c=gc();
    while(!isdigit(c)){if(c=='-')f=1;c=gc();}
    while(isdigit(c)){ret=ret*10+c-48;c=gc();}
    if(f)return -ret;return ret;
}

const int maxn=1e7+10;
const ll mod=998244353;

struct node{
    ll t,p;
    bool operator < (const node& a) const{
        return t<a.t;
    }
}a[3005];

ll sum[maxn],dp[maxn],f[maxn];
ll fac[maxn];

ll quick_pow(ll a,ll b,ll mod){
    ll ans=1;
    while(b){
        if(b&1) ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}

ll inv(ll a){
    return quick_pow(a,mod-2,mod);
}

ll C(ll n,ll m){
    if(m>n) return 0;
    if(n<0||m<0) return 0;
    return fac[n]*inv(fac[m])%mod*inv(fac[n-m])%mod;
}

int main(){
    #ifdef io
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif

    ll L,d,m;
    scanf("%lld%lld%lld",&L,&d,&m);
    f[0]=sum[0]=1;
    fac[0]=1ll;
    rep(i,1,maxn-1) fac[i]=fac[i-1]*(ll)i%mod;
    rep(i,1,L){
        if(i<d) f[0]=0;
        else {
            f[i]=sum[i-d];
        }
        sum[i]=(sum[i-1]+f[i])%mod;
    }
    rep(i,1,m) scanf("%lld%lld",&a[i].t,&a[i].p);
    sort(a+1,a+1+m);
    rep(i,1,m){
        dp[i]=C(a[i].p-d*a[i].t+a[i].t-1,a[i].t-1)%mod;
        rep(j,1,i-1) dp[i]=(dp[i]-dp[j]*C(a[i].p-a[j].p-d*(a[i].t-a[j].t)+a[i].t-a[j].t-1,a[i].t-a[j].t-1)%mod+mod)%mod;
    }
    ll ans=f[L];
    rep(i,1,m){
        ans=(ans-dp[i]*f[L-a[i].p]%mod+mod)%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值