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\n",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\n",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\n",ans);
    return 0;
}

 

posted @ 2018-08-18 20:53  radishえらい  阅读(215)  评论(0编辑  收藏  举报