2019中山大学程序设计竞赛

hdu6514Monitor

theme:给定n*m矩阵,有p个操作:将以(x1,y1),(x2,y2)为左上、右下的矩阵覆盖。之后q次询问:以(x1,y1),(x2,y2)为左上、右下的矩阵是否有全被覆盖?

solution:二维前缀和。先由一次前缀和(差分)计算出每个格子被覆盖的次数之和。然后将不为0的值 置为1,再进行前缀和就算出每个矩阵区域覆盖总数,与矩阵大小比较即可。

#include<bits/stdc++.h>
using namespace std;
#define far(i,t,n) for(int i=t;i<n;++i)
typedef long long ll;
typedef unsigned long long ull;

int a[10000010];

int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        memset(a,0,sizeof(a));
        int p;
        scanf("%d",&p);
        far(i,0,p)
        {
            int x1,y1,x2,y2,b,c,d,e;
            scanf("%d%d%d%d",&b,&c,&d,&e);
            x1=n-d;
            y1=c-1;
            x2=n-b;
            y2=e-1;
            //差分
            a[x1*m+y1]+=1;
            if(y2+1<m)
                a[x1*m+y2+1]-=1;
            if(x2+1<n)
                a[(x2+1)*m+y1]-=1;
            if(y2+1<m&&x2+1<n)
                a[(x2+1)*m+y2+1]+=1;
        }
        //前缀和求出每个小矩阵被覆盖次数
        far(i,0,n)
            far(j,0,m)
            {
                if(j-1>=0)
                    a[i*m+j]+=a[i*m+j-1];
                if(i-1>=0)
                    a[i*m+j]+=a[(i-1)*m+j];
                if(i-1>=0&&j-1>=0)
                    a[i*m+j]-=a[(i-1)*m+j-1];
            }


        far(i,0,m*n)
        {
            if(a[i])
                a[i]=1;
        }

       //前缀和,求区间矩阵被覆盖总次数
        far(i,0,n)
            far(j,0,m)
            {
                if(j-1>=0)
                    a[i*m+j]+=a[i*m+j-1];
                if(i-1>=0)
                    a[i*m+j]+=a[(i-1)*m+j];
                if(i-1>=0&&j-1>=0)
                    a[i*m+j]-=a[(i-1)*m+j-1];
            }

        int q;
        scanf("%d",&q);
        far(i,0,q)
        {
            int x1,y1,x2,y2,b,c,d,e;
            scanf("%d%d%d%d",&b,&c,&d,&e);
            x1=n-d;
            y1=c-1;
            x2=n-b;
            y2=e-1;
            int ans=a[x2*m+y2];
            if(y1-1>=0)
                ans-=a[x2*m+y1-1];
            if(x1-1>=0)
                ans-=a[(x1-1)*m+y2];
            if(x1-1>=0&&y1-1>=0)
                ans+=a[(x1-1)*m+y1-1];
            if(ans==(x2-x1+1)*(y2-y1+1))
                printf("YES\n");
            else
                printf("NO\n");
        }
    }
}
/*
6 6
3
2 2 4 4
3 3 5 6
5 1 6 2
2
3 2 5 4
1 5 6 5

*/

**hdu6512Triangle

theme:判断n个数中可否存在构成三角形的三个数。1<=n<=5*10^6,数值在int范围内

solution:对于n个数找构成三角形的三个数的题,按数据范围有不同解法。想法是将这n个数从小到大排序,(下标从0开始),之后从i=1开始,将i,i+1作为三角形较大的两边,判断它们的差(正值)d是否<a[i-1]即可,因为一旦<则满足条件,一旦>,则d更>i-1之前的数了(要使c-b<a,则找到c-b最小值和a最大值,由于c-b最小处可能取多个,所以我们就遍历每一个c-b即可,这样c-b最小即为每个相邻数之差)。这样做时间复杂度就是排序的时间o(nlog(n)),但该题n较大,又存在多组测试数据,o(nlog(n))关闭流同步才1994ms险过,所以考虑进一步优化:

由上述思路转化为两边之和大于第三边的话,相邻两数a[i],a[i+1]之和要是<a[i+2]也一定小于它后面的数,所以遍历时,极端情况就是1,2,3,5,8,13、、、刚好=情况,由斐波那契数列算出n到50左一点就超int了,所以可看n>=50时一定存在三个数(排序后)组成三角形。

#include <cstdio>
#include <list>
#include<iostream>
#include <algorithm>
using namespace std;
typedef long long ll;

int a[5000010];
ll fib[200];

int main()
{
    ios::sync_with_stdio(false);

//    fib[0]=1,fib[1]=1;
//    for(int i=2;i<=100;++i)
//        fib[i]=fib[i-1]+fib[i-2];
//    cout<<fib[50];

    int n;
    while(cin>>n)
    {
        for(int i=0;i<n;++i)
            cin>>a[i];
        if(n<2)
        {
            cout<<"NO\n";
            continue;
        }
        if(n>=50)
        {
            cout<<"YES\n";
            continue;
        }
        sort(a,a+n);
        int flag=0;
        for(int i=1;i<n-1;++i)
        {
            int b=a[i+1]-a[i];
            if(b<a[i-1])
            {
                flag=1;
                break;
            }
        }
        if(flag)
            cout<<"YES\n";
        else
            cout<<"NO\n";
    }
    return 0;
}

*hdu6518Clumsy Keke

theme:由矩阵形式给定三视图,一小格代表体积为1.问满足该三视图的图形的最大体积为多少?

                  

solution:题目输入并不是直接按主视图、左视图、俯视图角度给的,所以先确定一下x,y,z轴方向。想象一个由x*y*z的格子堆成的正方体。对于每一视图,如主视图,该格上有图形,则表示它及它后面的格子都可能放小正方体。这样由三个视图的&关系可以确定哪些格子上是可以放小正方体的。

#include <cstdio>
#include <list>
#include <cstring>
#include<iostream>
#include <algorithm>
using namespace std;
typedef long long ll;

int Front[110][110];
int Left[110][110];
int Top[110][110];
int ans[110][110][110];

int main()
{
    int mx,my,mz;
    while(~scanf("%d%d%d",&mx,&my,&mz))
    {
        for(int i=0;i<mx;++i)
            for(int j=0;j<my;++j)
                scanf("%d",&Front[i][j]);
        for(int i=0;i<my;++i)
            for(int j=0;j<mz;++j)
                scanf("%d",&Left[i][j]);
        for(int i=0;i<mz;++i)
            for(int j=0;j<mx;++j)
                scanf("%d",&Top[i][j]);

        for(int i=0;i<mx;++i)
            for(int j=0;j<my;++j)
                for(int z=0;z<mz;++z)
                    ans[i][j][z]=Front[i][j];
        for(int j=0;j<my;++j)
            for(int z=0;z<mz;++z)
               for(int i=0;i<mx;++i)
                    ans[i][j][z]&=Left[j][z];
        for(int z=0;z<mz;++z)
            for(int i=0;i<mx;++i)
                for(int j=0;j<my;++j)
                    ans[i][j][z]&=Top[z][i];

        int res=0;
        for(int i=0;i<mx;++i)
            for(int j=0;j<my;++j)
                for(int z=0;z<mz;++z)
                    res+=ans[i][j][z];
        printf("%d\n",res);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值