2019中山大学程序设计竞赛(重现赛)部分题解

题目链接:http://acm.hdu.edu.cn/search.php?field=problem&key=2019%D6%D0%C9%BD%B4%F3%D1%A7%B3%CC%D0%F2%C9%E8%BC%C6%BE%BA%C8%FC%A3%A8%D6%D8%CF%D6%C8%FC%A3%A9&source=1&searchmode=source

1004 Monitor(二维前缀和)

#include<bits/stdc++.h>
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define ll long long

#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define root l,r,rt
#define mst(a,b) memset((a),(b),sizeof(a))
#define pii pair<int,int>
#define fi first
#define se second
#define mk(x,y) make_pair(x,y)
const int mod=1e9+7;
const int maxn=1e7+100;
const int ub=1e6;
ll powmod(ll x,ll y){ll t; for(t=1;y;y>>=1,x=x*x%mod) if(y&1) t=t*x%mod; return t;}
ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}
/*
题目大意:
给定一个二维平面范围,
和p个监视区,q个偷盗区,
监视区和偷盗区形状是矩形,
q个询问问偷盗区是否被监控去完全覆盖.

题目分析:
二维差分,蛮套路的,
如果知道一维的情况那么二维不难,
比如(x,y),(p,q)给定这两个坐标点,
那么我们要想让这个矩形区域全置1,
把(x,y),(p+1,q+1)打上1的标记,
(x,q+1),(p+1,y)打上-1的标记,
然后做二维前缀和即可,
为了方便统计查询,再做一次前缀和,
查询时判断个数关系即可.
*/
int n,m,pp,qq;
ll a[maxn];
int s(int x,int y){
    return x*m+y-1;
}
int main(){
    while(scanf("%d%d",&n,&m)!=EOF){
        rep(i,0,n+1) rep(j,0,m+1) a[s(i,j)]=0;
        scanf("%d",&pp);
        rep(i,0,pp){
            int x,y,p,q;
            scanf("%d%d%d%d",&x,&y,&p,&q);
            a[s(x,y)]++,a[s(p+1,q+1)]++;
            a[s(x,q+1)]--,a[s(p+1,y)]--; 
        }
        rep(i,1,n+1) rep(j,1,m+1) a[s(i,j)]+=a[s(i-1,j)]+a[s(i,j-1)]-a[s(i-1,j-1)];  
        rep(i,1,n+1) rep(j,1,m+1) a[s(i,j)]=min(a[s(i,j)],1LL);
        rep(i,1,n+1) rep(j,1,m+1) a[s(i,j)]+=a[s(i-1,j)]+a[s(i,j-1)]-a[s(i-1,j-1)];  
        scanf("%d",&qq);
        rep(i,0,qq){
            int x,y,p,q;
            scanf("%d%d%d%d",&x,&y,&p,&q);
            if((1LL*(p-x+1)*(q-y+1))==(a[s(p,q)]-a[s(p,y-1)]-a[s(x-1,q)]+a[s(x-1,y-1)]))
                puts("YES");
            else puts("NO");
        }
    }
    return 0;
}

1008 模拟

#include<bits/stdc++.h>
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define ll long long

#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1

const int maxm=1e3+5;

int n,m,k;
int a[maxm][maxm],b[maxm][maxm],c[maxm][maxm];
int main(){
    while(scanf("%d%d%d",&n,&m,&k)!=EOF){
        int ans=0;
        rep(i,0,n) rep(j,0,m) scanf("%d",&a[i][j]);
        rep(i,0,m) rep(j,0,k) scanf("%d",&b[i][j]);
        rep(i,0,k) rep(j,0,n){
            scanf("%d",&c[i][j]);
            if(c[i][j]){
                rep(p,0,m) if(a[j][p]&&b[p][i]) ans++;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

1009 模拟

#include<bits/stdc++.h>
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define ll long long

const int maxn=103;
int nn,mm,kk;
char s[maxn][maxn],ans[maxn*15][maxn*15];
int main(){
    while(scanf("%d%d%d",&nn,&mm,&kk)!=EOF){
        rep(i,0,nn) scanf("%s",s[i]);
        rep(i,0,nn) rep(j,0,mm){
            for(int p=i*kk;p<i*kk+kk;p++) for(int q=j*kk;q<j*kk+kk;q++){
                ans[p][q]=s[i][j];
            }
        }
        rep(i,0,nn*kk){
            rep(j,0,mm*kk) printf("%c",ans[i][j]);
            puts("");
        }
    }
    return 0;
}

1011 线段树+思维+二分

#include<bits/stdc++.h>
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define ll long long

#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define root l,r,rt
#define mst(a,b) memset((a),(b),sizeof(a))
#define pii pair<int,int>
#define fi first
#define se second
#define mk(x,y) make_pair(x,y)
const int mod=1e9+7;
const int maxn=5e5+10;
const int ub=1e6;
const int INF=-1e9;
ll powmod(ll x,ll y){ll t; for(t=1;y;y>>=1,x=x*x%mod) if(y&1) t=t*x%mod; return t;}
ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}
/*
题目大意:
给定n和m个操作,
n代表序列长度,
每个操作给定一个区间,
代表把这个区间的人聚到一起,
其区间效率是这个区间中多少对人没有在之前的区间中
已经聚过.

题目分析:
线段树+思维转化,
我们可以对每个人,维护一个
数组代表其最远认识到右边的哪一个人,
比如如果区间1,4操作后,那么1最远可以认识到4,
考虑到相互性,我们只需要对每个人维护一端即可.
初始化每个人对应的就是他自己的位置.
更新不难,但查询有点困难,对于区间中数组值
大于右端点的,没有贡献,而在右端点之内的,产生与右端点之差的贡献,
我们无法通过普通的区间求和去表示这一关系.
考虑其数组值的单调性,不难发现我们定义的数组值始终是不减的,
那么我们就可以二分出最大的权重小于右端点的位置点,
有了这个点后就可以进行传统的区间更新了,
我们维护最大值和一个区间和,
注意懒惰标记的更新不是直接赋值而是直接取max,越大越好,
其他一些细节就是数据范围的问题了.
*/
int rt[maxn],minv[maxn<<2],x,y;
int maxv[maxn<<2],lazy[maxn<<2];
ll sum[maxn<<2];
void build(lrt){
    lazy[rt]=0;
    if(l==r){
        sum[rt]=maxv[rt]=l;
        return ;
    }
    int mid=l+r>>1;
    build(lson),build(rson);
    maxv[rt]=max(maxv[rt<<1],maxv[rt<<1|1]);
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void pushdown(lrt){
    int mid=l+r>>1;
    if(lazy[rt]){
        lazy[rt<<1]=max(lazy[rt<<1],lazy[rt]);
        lazy[rt<<1|1]=max(lazy[rt<<1|1],lazy[rt]);
        maxv[rt<<1]=max(maxv[rt<<1],lazy[rt]);
        maxv[rt<<1|1]=max(maxv[rt<<1|1],lazy[rt]);
        sum[rt<<1]=1LL*(mid-l+1)*maxv[rt<<1];
        sum[rt<<1|1]=1LL*(r-mid)*maxv[rt<<1|1];
        lazy[rt]=0;
    }
}
int getpos(lrt,int p){
    pushdown(root);
    if(l==r) return maxv[rt];
    int mid=l+r>>1;
    if(p<=mid) return getpos(lson,p);
    else return getpos(rson,p);
}
void update(lrt,int L,int R,int v){
    if(L<=l&&r<=R){
        maxv[rt]=max(maxv[rt],v);
        sum[rt]=1LL*(r-l+1)*maxv[rt];
        lazy[rt]=max(lazy[rt],v);
        return;
    }
    pushdown(l,r,rt);
    int mid=l+r>>1;
    if(L<=mid) update(lson,L,R,v);
    if(mid<R) update(rson,L,R,v);
    maxv[rt]=max(maxv[rt<<1],maxv[rt<<1|1]);
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}

ll getsum(lrt,int L,int R){
    if(L<=l&&r<=R) return sum[rt];
    pushdown(l,r,rt);
    int mid=l+r>>1;
    ll ans=0;
    if(L<=mid) ans+=getsum(lson,L,R);
    if(mid<R) ans+=getsum(rson,L,R);
    maxv[rt]=max(maxv[rt<<1],maxv[rt<<1|1]);
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
    return ans;
}
int n,m;
int main(){
    while(scanf("%d%d",&n,&m)!=EOF){
        build(1,n,1);
        rep(i,0,m){
            scanf("%d%d",&x,&y);
            int l=1,r=n,p=0;
            while(l<=r){
                int mid=l+r>>1;
                if(getpos(1,n,1,mid)<y){
                    p=mid;l=mid+1;
                }else{
                    r=mid-1;
                }
            }
            p=min(p,y);
            if(x<=p){
                printf("%lld\n",1LL*(p-x+1)*y-getsum(1,n,1,x,p));
                update(1,n,1,x,p,y);
            }else{
                puts("0");
            }
        }
    }
    return 0;
}

 1003 Reverse It(组合数学+消去重复度)

#include<bits/stdc++.h>
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define ll long long

#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define root l,r,rt
#define mst(a,b) memset((a),(b),sizeof(a))
#define pii pair<int,int>
#define fi first
#define se second
#define mk(x,y) make_pair(x,y)
const int mod=1e9+7;
const int maxn=1e2+100;
const int ub=1e6;
ll powmod(ll x,ll y){ll t; for(t=1;y;y>>=1,x=x*x%mod) if(y&1) t=t*x%mod; return t;}
ll gcd(ll x,ll y){
    if(y==0) return x;
    return gcd(y,x%y);
}
ll n,m;
char s[maxn][maxn];
ll C(ll x,int y){
    if(x<1LL*y) return 0;
    ll ans=1,tmp=1;
    for(int i=1;i<=y;i++){
        ans=ans*(x-i+1);
        tmp*=i;
    }
    return ans/tmp;
}
int main(){
    while(scanf("%lld%lld",&n,&m)!=EOF){
        rep(i,0,n) scanf("%s",s[i]);
        ll ret=C(n+1,2)*C(m+1,2);
        ret=C(ret,2)+1;
        ret-=4LL*C(n+1,3)*C(m+1,3);///对角
        ret-=8LL*C(n+1,3)*C(m+1,3);///斜率
        ret-=(2LL*C(n+1,2)*C(m+1,4)+2LL*C(n+1,4)*C(m+1,2));///分割
        ret-=(n+m-3)*(C(n+1,2)*C(m+1,2));
        printf("%lld\n",ret);
    }
    return 0;
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值