zoukankan      html  css  js  c++  java
  • 2018.8.18提高B组模拟试题

    今天出题人出了三道看似很难,实则暴力就能过的题...我...

    T1 题意简述:jzoj3452

    Description

    鸡腿是CZYZ的著名DS,但是不想学数学的DS不是好GFS,所以鸡腿想通过提高数学水平来增强他的GFS气质!虽然你对鸡腿很无语,但是故事的设定是你帮助鸡腿增强了GFS气质,所以现在你必须教鸡腿学数学! 

    鸡腿想到了一个很高(sha)明(bi)的问题,在 N 条水平线与 M 条竖直线构成的网格中,放 K 枚石子,每个石子都只能放在网格的交叉点上。问在最优的摆放方式下,最多能找到多少四边平行于坐标轴的长方形,它的四个角上都恰好放着一枚石子。 

    Input

    一行输入三个正整数N,M,K。 

    Output

    一行输出一个正整数,表示最多的满足条件的长方形数量。 

    Data Constraint

    对于50%的数据0 < N, M ≤ 30; 

    对于100%的数据0 < N, M ≤ 30000;K ≤ N*M。

       解题思路:这题...看似是一道dp...实则是一道结论题...

                 可以发现矩形数量最多当且仅当石子满足以下两种摆放顺序之一:

                 XXX...XXX   或   XXX...XXXXX

                 XXX...XXX        XXX...XXX

                 ...              ...

                 XXX...XXX        XXX...XXX

                 XX

                 因此只需枚举除最后一行(列)外每行(列)有几个石子,然后计算即可。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #define ll long long
    using namespace std;
    ll n,m,k,ans;
    int main()
    {
        freopen("rectangle.in","r",stdin);
        freopen("rectangle.out","w",stdout);
        scanf("%lld%lld%lld",&n,&m,&k);
        for(ll i=2;i<=m;i++)
        {
            ll x=k/i,y=k%i;
            if(!x) break;
            if(x>n||(x==n&&y)) continue;
            ll tmp=x*(x-1)/2*i*(i-1)/2+y*(y-1)/2*x;
            ans=max(ans,tmp);
        }
        for(ll i=2;i<=n;i++)
        {
            ll x=k/i,y=k%i;
            if(!x) break;
            if(x>m||(x==m&&y)) continue;
            ll tmp=x*(x-1)/2*i*(i-1)/2+y*(y-1)/2*x;
            ans=max(ans,tmp);
        }
        printf("%lld
    ",ans);
        return 0;
    }

    T2 题意简述:jzoj3453

    Description

    你应该知道无向图的连通块的数量,你应该知道如何求连通块的数量。当你兴奋与你的成就时,破坏王Alice拆掉了图中的边。当她发现,每删去一条边,你都会记下边的编号,同时告诉她当前连通块的个数。 

     然而,对边编号简直就是个悲剧,因为Alice为了刁难你,拆掉编号从l到r的边,当然你需要做的事情就是求连通块的个数。如果你答对了,Alice会把拆掉的边装好,迚行下一次破坏。如果你无法完成这个任务,Alice会彻底毁了你的图。 

    进行完足够多次之后,Alice觉得无聊,就玩去了,而你却需要继续做第三题。

    Input

    第一行两个整数n,m,表示点数和边数。 

    之后m行每行两个整数x,y,表示x与y之间有无向边。(按读入顺序给边编号,编号从1开始) 

     一行一个整数k,表示Alice的破坏次数。 

     之后k行,每行两个整数l,r。 

    Output

     k行,每行一个整数。 

    Data Constraint

    对于30%的数据,n<=100,k<=10 

    对于60%的数据,k<=1000 

    对于100%的数据,n<=500,m<=10000,k<=20000,1<=l<=r<=m

       解题思路:这道题我的第一反应是先找图上的简单环,然后枚举每条被删除的边看是否能每个环分到一

                 条边或分不到边,多余的未分配边数即等于增加的连通块数。

                 虽然想法是完全正确的,但是找简单环可一点也不容易...到考试结束我也没写出来。

                 看过题解,有种想吐血的冲动。题解给出的方法如下:

                 记录两个并查集数组分别表示加入前i条边与后i条边后的连通块情况。询问时合并即可。

                 ...我还是太蠢了。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    int n,m,k,fa[501],prefa[10002][501],suffa[10002][501];
    struct uio{
        int a,b;
    }edge[10001];
    int getprefa(int x,int id)
    {
        if(x==prefa[id][x]) return x;
        return prefa[id][x]=getprefa(prefa[id][x],id);
    }
    int getsuffa(int x,int id)
    {
        if(x==suffa[id][x]) return x;
        return suffa[id][x]=getsuffa(suffa[id][x],id);
    }
    int getfa(int x)
    {
        if(x==fa[x]) return x;
        return fa[x]=getfa(fa[x]);
    }
    int main()
    {
        freopen("connect.in","r",stdin);
        freopen("connect.out","w",stdout);
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            prefa[0][i]=suffa[m+1][i]=i;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&edge[i].a,&edge[i].b);
            memcpy(prefa[i],prefa[i-1],sizeof(prefa[i-1]));
            int xx=getprefa(edge[i].a,i);
            int yy=getprefa(edge[i].b,i);
            if(xx!=yy) prefa[i][yy]=xx;
        }
        for(int i=m;i;i--)
        {
            memcpy(suffa[i],suffa[i+1],sizeof(suffa[i+1]));
            int xx=getsuffa(edge[i].a,i);
            int yy=getsuffa(edge[i].b,i);
            if(xx!=yy) suffa[i][yy]=xx;
        }
        scanf("%d",&k);
        for(int i=1;i<=k;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            memcpy(fa,prefa[u-1],sizeof(prefa[u-1]));
            for(int j=1;j<=n;j++)
            {
                int xx=getfa(j);
                int yy=getsuffa(j,v+1);
                int zz=getfa(yy);
                if(xx!=zz) fa[xx]=zz;
            }
            int tmp=0;
            for(int j=1;j<=n;j++)
                if(getfa(fa[j])==j) tmp++;
            printf("%d
    ",tmp);
        }
        return 0;
    }

    T3 题意简述:jzoj3450

    Description

    作为地质学家的JIH,为了绘制地图进行了野外考察。考察结束,他得到了一张n*m的地面高度地图。为了科学研究,JIH定义了一种山峰叫做d-山峰。一个高度为h地点被称作d-山峰,只有满足从这里出发,在不经过小于等于h-d的地点的前提下无法达到比它更高的地方。JIH正纠结于怎么分礼物,标出d-山峰的任务就交给你了。

    Input

    第一行n,m,d

    第二行开始,有一个n*m的矩阵表示地图,用空格隔开。

    Output

    输出d-山峰的个数。

    Data Constraint

    30% n,m<=10

    100% n,m<=500

     

       解题思路:各位大佬猜这道题的正解是什么?

                 揭晓答案:暴力dfs+一个愚蠢的剪枝。

                 ...这种题目出现在提高B组真的好吗?

                 至于那个愚蠢的剪枝,为了不浪费各位大佬的时间,博主稍微解释一下:

                 不必每搜一个点就把vis数组清空一次,只需把标记打成这个点的标号即可。

                 ...足够愚蠢吧?

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    int n,m,k,ans,flg,a[501][501],vis[501][501];
    void dfs(int x,int y,int num,int id)
    {
        vis[x][y]=id;
        if(a[x][y]>num) {flg=1;return;}
        if(x!=1&&a[x-1][y]>num-k&&vis[x-1][y]!=id) dfs(x-1,y,num,id);
        if(flg) return;
        if(x!=n&&a[x+1][y]>num-k&&vis[x+1][y]!=id) dfs(x+1,y,num,id);
        if(flg) return;
        if(y!=1&&a[x][y-1]>num-k&&vis[x][y-1]!=id) dfs(x,y-1,num,id);
        if(flg) return;
        if(y!=m&&a[x][y+1]>num-k&&vis[x][y+1]!=id) dfs(x,y+1,num,id);
        if(flg) return;
    }
    int main()
    {
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",&a[i][j]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {
                flg=0;
                dfs(i,j,a[i][j],(i-1)*m+j);
                if(!flg) ans++;
            }
        printf("%d
    ",ans);
        return 0;
    }
  • 相关阅读:
    Spring+Spring MVC+Hibernate框架搭建实例
    数据结构:串
    为什么java中用枚举实现单例模式会更好
    验证码原理分析及实现
    Servlet监听器——实现在线登录人数统计小例子
    IntelliJ IDEA 远程调试 Tomcat 的方法
    SQL注入的一些技巧分享
    Java中String与byte[]的转换
    JavaSE基础:集合类
    二叉树的非递归遍历
  • 原文地址:https://www.cnblogs.com/water-radish/p/9498567.html
Copyright © 2011-2022 走看看