【11.08】一套不太难的题

本文总结了NOIP2016复赛模拟训练中的三道题目,包括干草(outofhay)、分解数和推冰块问题的解决思路及代码实现。针对每道题目进行了详细解析,并分享了作者的解题经验和教训。
                **CCF 全国信息学奥林匹克联赛(NOIP2016)复赛
                                模拟训练 9.11**

这次考试发现了很多问题(170)
最大的问题就是写程序的时候不细心,T1很水的二分+bfs(MST?)但我的n写成m,浪费了很多时间。
幸亏没有过了样例就就不管了,后天的考试中多造几组数据(现在还不会对拍)
T2 像这样很难想出正解的,骗到尽可能多的分之后下一题,不要浪费太多时间
T3 一个广搜拿50分,这种能拿的分不能丢。正解是hash处理10^9 的数据
%%%Asia 连续两次机房rank1!%%

(请选手务必仔细阅读本页内容)

1、干草(outofhay)

这里写图片描述

水二分

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=2010;
const int MAXM=20010;
int n,m,x,y,z;
struct Edge{
    int next,to,dis;
}edge[MAXM];
int bian[MAXM];
int team[MAXM];
bool vis[MAXN];

int head[MAXN],num_edge;
void add_edge(int from,int to,int dis)
{
    edge[++num_edge].next=head[from];
    edge[num_edge].to=to;
    edge[num_edge].dis=dis;
    head[from]=num_edge;
}

bool judge(int len)
{
    int tot=1;
    memset(vis,0,sizeof(vis));
    vis[1]=1; team[1]=1;
    int front=0,tail=1;
    while (front<=tail)
    {
        int now=team[++front]; vis[now]=1;
        for (int i=head[now]; i!=0; i=edge[i].next)
        {
            if (!vis[edge[i].to]&&edge[i].dis<=len)
            {
                vis[edge[i].to]=1;
                tot++;
                team[++tail]=edge[i].to;
            }
        }
    }
    if (tot==n) return true;//
    else return false;
}

int main()
{
    freopen("outofhay.in","r",stdin);
    freopen("outofhay.out","w",stdout);

    scanf("%d%d",&n,&m);
    for (int i=1; i<=m; i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        add_edge(x,y,z); add_edge(y,x,z);
        bian[i]=z;
    }
    sort(bian+1,bian+1+m);
    int l=0,r=m;
    while (l<r)
    {
        int mid=(l+r)/2;
        if (judge(bian[mid])) r=mid;
        else l=mid+1;
    }
    printf("%d",bian[l]);
    fclose(stdin); fclose(stdout);
    return 0;
}
/*
4 6
1 2 50 
1 3 1
2 3 1
1 4 200
3 4 70
4 2 150

3 3
1 2 23
2 3 1000
1 3 43

43
*/

2.分解数
(dec.cpp/c/pas)
【问题描述】
Dpstr学习了动态规划的技巧以后,对数的分解问题十分感兴趣。
Dpstr用此过程将一个正整数x分解成若干个数的乘积:一开始令集合A中只有一个元素x,每次分解时从A中取一个元素a并找出两个大于1且互质的整数p,q,要求pq=a,然后将a分解成两个元素p和q,也就是从A中删去a并加入p和q。Dpstr把正整数x用该过程能分解的次数的最大值称为x的分解数。
例如66的分解数为2,因为最多分解2次。一种分解过程为:一开始A={66},第1次将66分解为11×6,此时A={11,6},第2次将6分解为2×3,此时A={11,2,3},之后无法分解。还可以知道,11,2,3的分解数均为0,因为它们一开始就无法分解。
不过只分解一个数对Dpstr来说不够有趣。Dpstr生成了一个包含n个正整数的数列a1, a2, …, an,请你回答有多少对正整数(l,r)满足1≤l≤r≤n且lcm(al, al+1, …, ar-1, ar)的分解数恰为k。其中lcm(al, al+1, …, ar-1, ar)表示数列从第l项到第r项的所有数的最小公倍数,特别地,当l=r时,lcm(al)=al。由于答案可能很大,只需输出满足条件的正整数对个数除以10,007的余数。

【输入格式】
输入文件名为dec.in。
第一行包含两个正整数n,k,意义如题目所示。
第二行包含n个用空格隔开的正整数,分别表示a1, a2, …, an。

【输出格式】
输出文件名为dec.out。
输出一行一个整数,表示满足条件的正整数对个数除以10,007的余数。

【输入输出样例1】

            dec.in                      dec.out
            3 1
            2 3 6                           4

见选手目录下的dec/dec1.in和dec/dec1.ans。

【输入输出样例1说明】
考虑所有满足1≤l≤r≤4的正整数对(l,r):
1、对于(1,1),lcm(2)=2,分解数为0;
2、对于(2,2),lcm(3)=3,分解数为0;
3、对于(3,3),lcm(6)=6,分解数为1;
4、对于(1,2),lcm(2,3)=6,分解数为1;
5、对于(2,3),lcm(3,6)=6,分解数为1;
6、对于(1,3),lcm(2,3,6)=6,分解数为1。
其中,6的分解数为1是因为{6}可以分解为{2,3},之后无法分解。
因此共有4对正整数(l,r)满足条件。

【输入输出样例2】
dec.in dec.out
10 2
2 3 2 6 15 5 5 5 2 3 29
见选手目录下的dec/dec2.in和dec/dec2.ans。

【输入输出样例3】
见选手目录下的dec/dec3.in和dec/dec3.ans。

【数据规模与约定】
对于20%的数据,1≤n≤10,1≤k≤5,1≤ai≤20;
对于40%的数据,1≤n≤100,1≤k≤10,1≤ai≤100;
对于60%的数据,1≤n≤1,000,1≤k≤1,000,1≤ai≤100,000;
对于100%的数据,1≤n≤1,000,000,1≤k≤5,000,000,1≤ai≤10,000,000。

参悟的学姐的blog半天没参悟出来。只能等到noip之后了:

zyf2000

3.推冰块
(ice.cpp/c/pas)
【问题描述】
Dpstr最近迷上了推冰块。冰地是一个n行m列的网格区域,第i行第j列的格子记为(i,j),也就是左上角为(1,1),右下角为(n,m)。每个格子可能是冰面、障碍物、减速带三者之一。其中,冰地外围(即第0行、第n+1行、第0列、第m+1列)的所有格子均有障碍物。除此之外,冰地内共有k个障碍物和减速带,其余格子为冰面。
初始时,有一个冰块位于(1,1)处。Dpstr每次可以选择上、下、左、右四个方向之一推动该冰块,推动后该冰块将一直沿此方向移动,直到冰块所在的格子为减速带,或冰块沿运动方向的下一个格子为障碍物时,冰块将停止运动。一旦冰块停在减速带上,该减速带即消失。
如下图,当n = 5,m = 5时,若冰块位于(3,1),且(3,4)处有一个障碍物,那么向右推动冰块(图1中A处),冰块将停在(3,3)(图1中B处);而如果(3,4)处不是障碍物而是减速带,那么冰块将停在(3,4)上(图2中B处),之后(3,4)处的减速带就会消失。
这里写图片描述

Dpstr希望通过尽量少的推动次数使得冰块停在(n,m)处。请计算Dpstr至少要推多少次冰块。

【输入格式】
输入文件名为ice.in。
输入的第一行包含三个整数n,m,k,表示冰地大小为n × m,冰地内障碍物和减速带共有k个。
接下来k行,每行包含三个整数xi,yi,ti,描述一个障碍物或减速带。若ti = 0,则表示(xi,yi)处有一个障碍物;若ti = 1,则表示(xi,yi)处有一个减速带。保证同一位置不会有多个障碍物或减速带,且(1,1)处和(n,m)处不会有障碍物或减速带。

【输出格式】
输出文件名为ice.out。
输出仅一行,包含一个整数,表示将冰块推到(n,m)处的最少推动次数。
数据保证至少存在一种将冰块推到(n,m)处的方案。

【输入输出样例1】
ice.in ice.out
4 3 2
1 2 1
4 2 0 3
见选手目录下的ice/ice1.in和ice/ice1.ans。

【输入输出样例1说明】
如图3,至少推3次,一种方案为依次向右、向右、向下推动。

这里写图片描述

【输入输出样例2】
见选手目录下的ice/ice2.in和ice/ice2.ans。

【数据规模与约定】
对于30%的数据,2≤n≤5,2≤m≤5,0≤k≤5;
对于50%的数据,2≤n≤1,000,2≤m≤1,000,0≤k≤1,000;
对于70%的数据,2≤n≤50,000,2≤m≤50,000,0≤k≤50,000;
对于100%的数据,2≤n≤1,000,000,000,2≤m≤1,000,000,000,0≤k≤50,000,1≤xi≤n,1≤yi≤m,0≤ti≤1。

能一眼看出是广搜吧
code(50)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int MAXN=5001;
int dx[5]={0,0,0,1,-1},
    dy[5]={0,1,-1,0,0};
//障碍物 1 减速带 2

int n,m,k;
int g[MAXN][MAXN];
struct Node{
    int x,y,step;
};
queue <Node> que;

void bfs()
{
    while (!que.empty())
    {
        Node now=que.front(); que.pop();
        for (int i=1; i<=4; i++)
        {
            int xx=now.x; int yy=now.y;
            bool flag=false;
            while (xx+dx[i]>=1&&xx+dx[i]<=n&&yy+dy[i]>=1&&yy+dy[i]<=m&&g[xx+dx[i]][yy+dy[i]]!=1)
            {
                xx+=dx[i]; yy+=dy[i];
                if (g[xx][yy]==2) {g[xx][yy]=0; flag=true; break;}
            }
            if (xx==now.x&&yy==now.y) continue;
            if (!flag)
            {
                if (g[xx][yy]==-1) continue;
                g[xx][yy]=-1;
            }/**/
            if (xx==1&&yy==1) continue;
            if (xx==n&&yy==m) {printf("%d",now.step+1); return;}
            Node tmp;
            tmp.x=xx; tmp.y=yy; tmp.step=now.step+1;
            que.push(tmp);
        }
    }
}

int main()
{
//  freopen("ice.in","r",stdin);
//  freopen("ice.out","w",stdout);

    scanf("%d%d%d",&n,&m,&k);
    for (int i=1; i<=k; i++)
    {
        int x,y,t;
        scanf("%d%d%d",&x,&y,&t);
        if (t==0) g[x][y]=1;
        if (t==1) g[x][y]=2;
    }
    Node stat;
    stat.x=1; stat.y=1; stat.step=0;
    que.push(stat);
    bfs();
    return 0;
}
/*
4 3 2
1 2 1
4 2 0
3
*/

正解:(写ci了版,也是等到noip后了)

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
const int MAXN=1e5+7;
int dx[5]={0,0,0,1,-1},
    dy[5]={0,1,-1,0,0};
int n,m,k,step[MAXN],ha[MAXN];
struct Zhang{
    int x,y,z;
}a[MAXN],b[MAXN];
struct Node{
    int x,y;
};
queue <Node> que;

bool compA(Zhang a,Zhang b)//上左优先
{
    if (a.y!=b.y) return a.x<b.x;
    else return a.y<b.y;
}
bool compB(Zhang a,Zhang b)//左上优先
{
    if (a.x!=b.x) return a.y<b.y;
    else return a.x<b.x;
}

int hash(int x,int y)
{
    int k=(x-1)*m+y;//k这个数是不能变的 
    int t=k%MAXN;
    while (ha[t]&&ha[t]!=k) t++;
    return ha[t]=t;//返回的是t 在hash中的位置 
}

void judge(Node tmp)
{
    if (tmp.x==n&&tmp.y==m)
    {
        int h=hash(tmp.x,tmp.y);
        printf("%d",step[h]);
        exit(0);
    }
}

void find_LR(Node now)
{
    int l=0,r=k;
//find Left
    while (l<r)
    {
        int mid=(l+r)/2;
        if (a[mid].x>now.x||(a[mid].x==now.x&&a[mid].y>=now.y)) r=mid;
        else l=mid+1;
    }
    int p=hash(now.x,now.y);
    if (a[l].x!=now.x)//左边没有障碍物,可以一直走到最左边 
    {
        Node tmp; tmp.x=now.x; tmp.y=1;
        int to=hash(tmp.x,1);//在hash表里面标记这个点已经走过了 
        if (step[to]<0)
        {
            step[to]=step[p]+1;
            que.push(tmp); judge(tmp);
        }
    }
    else
    {
        Node tmp; tmp.x=now.x; tmp.y=now.y;
        if (a[l].z==0) tmp.y++;
        int to=hash(tmp.x,tmp.y);
        if (step[to]<0)
        {
            step[to]=step[p]+1;
            que.push(tmp); judge(tmp);
        }
    }
//find Right
    l=0; r=k;
    while(l<r)
    {
        int mid=(l+r)/2;
        if (a[mid].x<now.x||(a[mid].x==now.x&&a[mid].y<=now.y)) l=mid+1;
        else r=mid;
    }
    if (a[l].x!=now.x)
    {
        Node tmp; tmp.x=now.x; tmp.y=m;
        int to=hash(tmp.x,tmp.y);
        if (step[to]<0)
        {
            step[to]=step[p]+1;
            que.push(tmp); judge(tmp);
        }
    }
    else
    {
        Node tmp; tmp.x=a[l].x; tmp.y=a[l].y;
        if (a[l].z) tmp.y--;
        int to=hash(tmp.x,tmp.y);
        if (step[to]<0)
        {
            step[to]=step[p]+1;
            que.push(tmp); judge(tmp);
        }
    }
}

void find_FB(Node now)
{
    int p=hash(now.x,now.y);
//find Front
    int l=0; int r=k;
    while (l<r)
    {
        int mid=(l+r)>>1;
        if (b[mid].y>now.y||(b[mid].y==now.y&&b[mid].x>=now.x)) r=mid;
        else l=mid+1;
    }
    if (a[l].y!=now.y)
    {
        Node tmp; tmp.y=now.y; tmp.x=1;
        int to=hash(tmp.x,tmp.y);
        if (step[to]<0)
        {
            step[to]=step[p]+1;
            que.push(tmp); judge(tmp);
        }
    }
    else
    {
        Node tmp; tmp.x=now.x; tmp.y=now.y;
        if (b[l].z) tmp.x--;
        int to=hash(tmp.x,tmp.y);
        if (step[to]<0)
        {
            step[to]=step[p]+1;
            que.push(tmp); judge(tmp);
        }
    }
//find Behind
    l=0; r=k;
    while (l<r)
    {
        int mid=(l+r)>>1;
        if (b[mid].y<now.y||(b[mid].y==now.y&&b[mid].x<=now.x)) l=mid+1;
        else r=mid;
    }
    if (b[l].y!=now.y)
    {
        Node tmp; tmp.x=n; tmp.y=now.y;
        int to=hash(tmp.x,tmp.y);
        if (step[to]<0)
        {
            step[to]=step[p]+1;
            que.push(tmp); judge(tmp);
        }
    }
    else
    {
        Node tmp; tmp.x=b[l].x; tmp.y=b[l].y;
        if (b[l].z) tmp.x++;
        int to=hash(tmp.x,tmp.y);
        if (step[to]==-1)
        {
            step[to]=step[p]+1;
            que.push(tmp); judge(tmp);
        }
    }
}

void bfs()
{
    Node stat; stat.x=1; stat.y=1; que.push(stat);
    step[hash(1,1)]=0;
    while (!que.empty())
    {
        Node now=que.front(); que.pop();
        find_LR(now);
        find_FB(now);
    }
}

int main()
{
    freopen("ice.in","r",stdin);
    freopen("ice.out","w",stdout);

    memset(step,-1,sizeof(step));
    scanf("%d%d%d",&n,&m,&k);
    for (int i=1; i<=k; i++)
    {
        scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
        b[i].x=a[i].x; b[i].y=a[i].y; b[i].z=a[i].z;
    }
    sort(a+1,a+1+k,compA);
    sort(b+1,b+1+k,compB);
    bfs();

    return 0;
}
/*
4 3 2
1 2 1
4 2 0
3
*/

PS:明天早上8:30就要发车去日照。真有一种上战场的赶脚呢。
祝我好运

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值