HGOI NOIP模拟4 题解

NOIP国庆模拟赛Day5 题解

T1 马里奥

题目描述

马里奥将要参加 NOIP 了,他现在在一片大陆上,这个大陆上有着许多浮空岛,并且其中一座浮空岛上有一个传送门,马里奥想要到达传送门从而前往 NOIP的考场。

从一座浮空岛出发,马里奥可以到达一个在水平方向和这个浮空岛相接的另一个浮空岛,他还可以使用梯子到达在这个浮空岛正上方或正下方的另一座浮空岛,但是这两个浮空岛的高度差不能超过梯子的长度。

现在,马里奥希望用最短的梯子到达传送门,请你输出梯子的最短长度。

我们把浮空岛抽象成一个二维平面,’#’代表浮空岛,’_’代表空中。

两个整数 x,y 代表传送门所在的行数和列数。

保证最下方一行全部为’#’,且传送门所在位置为’#’,

马里奥一开始在最左下方的那个浮空岛上。

输入格式

第一行两个整数 n,m,表示输入平面的行数和列数。

接下来 n 行每行一个包含 m 个字符的字符串,表示这个二维平面。

最后一行两个整数 x,y 表示传送门所在的行和列。

输出格式

输出一行一个整数,表示梯子的最小长度

样例输入输出

Simple input #1:

5 8

####____

___#_###

###__#__

______#_

########

2 4

Simple output#1:

2

数据范围和提示

对于 70%的数据:1≤n,m≤100,1≤x≤n,1≤y≤m。

对于 100%的数据:1≤n,m≤1,000,1≤x≤n,1≤y≤m。

本题时限1s,空间256MB

题解

Subtask1: 暴力dfs不优化暴力(具体不知道多暴力,反正我这个就是最暴力了)

Subtask2: 二分答案+BFS

时间复杂度 O(log n * n^2)

没什么好说了吧

代码(Subtask2 100pts)

# include<bits/stdc++.h>
# define Rint register int
using namespace std;
const int MAXN=1e3+10;
int n,m,tt=1;
char s[MAXN];
int a[MAXN][MAXN],x,y;
struct rec{ int x,y;};
bool vis[MAXN][MAXN];
bool check(Rint Mid)
{
    queue<rec>q;
    rec s; s.x=n; s.y=1;
    q.push(s);
    memset(vis,false,sizeof(vis));
    while (!q.empty()) {
        rec u=q.front();q.pop(); rec v;
        if (u.x==x&&u.y==y) return true; 
        v.x=u.x;v.y=u.y-1;
        if (v.x>=1&&v.x<=n&&v.y>=1&&v.y<=m&&a[v.x][v.y]&&(!vis[v.x][v.y])) {
            if (v.x==x&&v.y==y) return true;
            vis[v.x][v.y]=1;
            q.push(v);
        }
        v.x=u.x;v.y=u.y+1;
        if (v.x>=1&&v.x<=n&&v.y>=1&&v.y<=m&&a[v.x][v.y]&&(!vis[v.x][v.y]))  {
            if (v.x==x&&v.y==y) return true;
            vis[v.x][v.y]=1;
            q.push(v);
        }
        for (int i=1;i<=Mid;i++) {
            v.x=u.x+i; v.y=u.y;
            if (v.x>=1&&v.x<=n&&v.y>=1&&v.y<=m&&a[v.x][v.y]&&(!vis[v.x][v.y])) {
                if (v.x==x&&v.y==y) return true;
                vis[v.x][v.y]=1;
                q.push(v);
            }
            v.x=u.x-i; v.y=u.y;
            if (v.x>=1&&v.x<=n&&v.y>=1&&v.y<=m&&a[v.x][v.y]&&(!vis[v.x][v.y]))  {
                if (v.x==x&&v.y==y) return true;
                vis[v.x][v.y]=1;
                q.push(v);
            }
        }
    }
    return false;
}
int main()
{
    freopen("mario.in","r",stdin);
    freopen("mario.out","w",stdout);
    scanf("%d%d
",&n,&m);
    for(Rint i=1;i<=n;i++) {
        cin>>s; 
        int len=strlen(s);
        for (Rint j=0;j<len;j++)
         if (s[j]=='#') a[i][j+1]=1;
         else a[i][j+1]=0;
    }
    scanf("%d%d",&x,&y);    
    int L=0,R=n,Ans;
    while (L<=R) {
        int M=(L+R)/2;
        if (check(M)) Ans=M,R=M-1; 
        else L=M+1;
    }
    printf("%d
",Ans);
    return 0;
 } 

T2 祭司

题目描述

马里奥在你的帮助下成功地进入了传送门,但在传送途中,传送门出了一些 故障,马里奥被传送到了一座宏伟的神殿。神殿的祭司愿意帮助马里奥修复传送 门,但是祭司现在正忙于解读古代的魔法典籍,他希望马里奥能帮他解读。古代 典籍中给出了一些变量,每个变量都有一个可能的取值范围,祭司需要把这些变 量分成两组。对于一种划分方案,对这两组变量分别求和之后做差,可能得到的 差值的绝对值的最大值就是这次划分的评级。而那个评级最小的划分是解密的关 键,祭司想让马里奥求出这个最小的评级是多少,但马里奥已经没有多少时间了, 于是他找到了你来帮忙算出这个数字。

输入格式

第一行一个整数 n,表示给出的变量个数。

接下来 n 行每行两个整数,

第 i 行的两个数 li,ri,表示第 i 个变量的取值范围 为[li, ri]。

输出格式

输出应该包含一行一个整数,表示评级的最小值。

输入输出样例

Simple input:

4

1 3

3 6

2 4

4 5

Simple output:

5

Explain:

第一和第四个变量一组,第二和第三个变量一组。

当四个变量分别取值为 (1,6,4,4)时取得该情况下的最大值 5。

数据规模和约定

对于 70%的数据:1≤n≤10。

对于 100%的数据:1≤n,m≤200;0≤li,ri≤200。

本题时限1s,空间256MB

题解:

Subtask1:理解题意暴力dfs

时间复杂度O(2^n)

代码(Subtask1 70pts):

# include <bits/stdc++.h>
using namespace std;
const int MAXN=2e2+10; 
int n,t[MAXN],ans;
struct rec{
    int l,r;
}a[MAXN];
void check()
{
    int t1,t2,Max=0; t1=t2=0;
    for (int i=1;i<=n;i++)
     if (t[i]) t1+=a[i].l;
     else t2+=a[i].r;
    Max=max(Max,abs(t1-t2)); 
    t1=t2=0;
    for (int i=1;i<=n;i++)
     if (t[i]) t1+=a[i].r;
     else t2+=a[i].l;
    Max=max(Max,abs(t1-t2)); 
    ans=min(ans,Max);
}
void dfs(int dep)
{
    if (dep==n+1) { check(); return; }
    t[dep]=0; dfs(dep+1);
    t[dep]=1; dfs(dep+1);
}
int main()
{
    freopen("priest.in","r",stdin);
    freopen("priest.out","w",stdout);
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
     scanf("%d%d",&a[i].l,&a[i].r);
    ans=INT_MAX;
    dfs(1);
    printf("%d
",ans);
    return 0;
}

Subtask2:Dp

    Min1 表示A集合的上界之和

    Min2 表示B集合的上界之和

    Max1 表示A集合的下界之和

    Max2 表示B集合的下界之和

    Sum1 表示A集合元素上下界之和

    Sum2 表示B集合元素上下界之和

    答案就是求 max(|Min1-Max2|,|Min2-Max1|)

    max(|Min1-Max2|,|Min2-Max1|)=max(|Min1+Max1-Max2-Max1|,|Max2+Min2-Max1-Max2|)

    =max(|Sum1-(Max1+Max2)|,|Sum2-(Max1+Max2)|);<--最小化这个

    其中 Max1+Max2为全集的上界,只需要维护Sum1,Sum2即可.

    f[i]表示A集合上下界为i是否可能

f[i]=f[i]|f[i-(a[j].l+a[j].r)];

复杂度O(n^3)

代码(Subtask 2 100pts):

/*
    Min1 表示A集合的上界之和
    Min2 表示B集合的上界之和
    Max1 表示A集合的下界之和
    Max2 表示B集合的下界之和
    Sum1 表示A集合元素上下界之和
    Sum2 表示B集合元素上下界之和
    答案就是求 max(|Min1-Max2|,|Min2-Max1|) 
    max(|Min1-Max2|,|Min2-Max1|)=max(|Min1+Max1-Max2-Max1|,|Max2+Min2-Max1-Max2|)
    =max(|Sum1-(Max1+Max2)|,|Sum2-(Max1+Max2)|);<--最小化这个 
    其中 Max1+Max2为全集的上界,只需要维护Sum1,Sum2即可.
    f[i]表示A集合上下界为i是否可能
    f[i]=f[i]|f[i-(a[j].l+a[j].r)]; 
*/
# include <bits/stdc++.h>
using namespace std;
const int MAXN=205;
struct rec{
    int l,r;
}a[MAXN];
bool f[40005],n;
int main()
{
    scanf("%d",&n);
    int Sum1=0,Sum2=0;
    for (int i=1;i<=n;i++)  {
        scanf("%d%d",&a[i].l,&a[i].r);
        Sum1+=a[i].l; Sum2+=a[i].r;
    }
    memset(f,false,sizeof(f));
    f[0]=true;
    for (int i=1;i<=n;i++)
     for (int j=40000;j>=a[i].l+a[i].r;j--)
      f[j]=f[j]||f[j-a[i].l-a[i].r];
     int ans=INT_MAX;
     for (int i=0;i<=40000;i++) {
        if (!f[i]) continue;
        ans=min(ans,max(abs(Sum1-i),abs(Sum2-i)));
     }   
     printf("%d
",ans);
    return 0;
}

T3 AK

题目描述

NOIP 考场上,马里奥顺利地切掉了前两题,他只要再切掉最后一题就可以 AK 了。最后一题是这样的:给你一个数字序列,每次查询一段区间的数字和, 并且把它们都变成原来的平方。马里奥瞬间就切掉了这道题,但他觉得这道题对 于别人来说太难了。出题人在和马里奥商量后,决定在询问时只要求返回模一个 数 c = 2305843008676823040 的结果。作为另一名 NOIP 选手的你也已经切掉了 前两题,你能够解决这个修改后的问题,顺利 AK 吗?

输入格式

第一行两个整数 n,m,表示数字个数和询问个数。

接下来一行 n 个数字 ai,表示序列的初始值。

接下来 m 行,每行两个整数 l,r 表示询问区间。

输出格式

对于每次询问,你需要输出一个整数,表示对应询问的结果。

输入输出样例

Simple input:

4 4

2 3 4 5

1 2

2 3

3 4

1 4

Simple output:

5

数据规模和约定

对于 60%的数据:1≤n,m≤2^10。

对于 100%的数据:1≤n,m≤2^16;0≤ai<c。

本题时限5s,空间256MB

题解

我的分块的第一题!!!

首先发现这个东西2305843008676823040明显不是质数

考虑到luogu的某一题模数是2的几次方,也不是质数在这里做手脚

又考虑到luogu有一道上帝造题的七分钟,几次以后就不变了,

由此联想最终发现若干次(考场上用不是质数推发现28次)以后模c的值就不变了

于是想到分块+并查集 维护一个暴力。

Sum[i] 表示第i个块所有值之和,L[i]表示块i的左边界,R[i]表示块i的右边界

a[i].val表示i点的点权,a[i].bl表示i在哪个块里 bl是块的数目

并查集维护i点的右边那个还未被更新的点的位置,便于快速移动

然后register int 还有inline 快读心里作用就可以极限数据1s以内了。

这道题时限5s好像稳过

Subtask 2 100pts)

# include <bits/stdc++.h>
# define int long long
# define Rint register long long 
using namespace std;
const int mo=2305843008676823040,E=30,MAXN=(2<<16)+1;
int n,m,bl;
int f[MAXN],L[MAXN],R[MAXN],sum[MAXN],cnt[MAXN];
struct rec{
    int bl,val;
}a[MAXN];
inline int read()
{
    int X=0,w=0; char c=0;
    while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
    while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
    return w?-X:X;
}
inline void print(Rint x)
{
    if(x>9) print(x/10);
    putchar(x%10+'0');
}
inline int father(Rint x)
{
    if (f[x]==x) return f[x];
    f[x]=father(f[x]);
    return f[x]; 
}
inline int mul(Rint a,Rint b)
{
    int res=0ll;
    while (b) {
        if (b&1) res=(res+a)%mo;
        a=(a<<1)%mo; b>>=1;
    }
    return res%mo;
}
inline int solve(Rint l,Rint r)
{
    int lb=a[l].bl,rb=a[r].bl,ans=0ll;
    if (lb==rb) {
        for (Rint i=l;i<=r;i++) 
         ans=(ans+a[i].val)%mo;
        return ans;
    }
    for (Rint i=l;i<=R[lb];i++) ans=(ans+a[i].val)%mo;
    for (Rint i=r;i>=L[rb];i--) ans=(ans+a[i].val)%mo;
    for (Rint i=lb+1;i<=rb-1;i++) ans=(ans+sum[i])%mo; 
    return ans%mo;
}
inline void update(Rint l,Rint r)
{
    for (Rint i=l;i<=r;i=father(i+1)) {
        if (i>r||i==0) break; 
        int nowbl=a[i].bl;
        sum[nowbl]=(sum[nowbl]-a[i].val+mo)%mo;
        a[i].val=mul(a[i].val,a[i].val)%mo; cnt[i]++;
        if (cnt[i]>E) f[i]=father(i+1);
        sum[nowbl]=(sum[nowbl]+a[i].val)%mo;
    }
}
signed main()
{
    freopen("ak.in","r",stdin);
    freopen("ak.out","w",stdout);
    scanf("%lld%lld",&n,&m); 
    for (Rint i=1;i<=n;i++) f[i]=i;
    memset(cnt,0,sizeof(cnt));
    bl=1; L[1]=1;
    int POA=sqrt(n)+1;
    for (Rint i=1;i<=n;i++)  {
       if (i%POA==0) { R[bl]=i-1; bl++; L[bl]=i; }
        a[i].val=read();
        a[i].bl=bl;
        sum[bl]=(sum[bl]+a[i].val)%mo;
    } 
    R[bl]=n;
    Rint l,r; 
    while (m--)  {
        l=read();r=read();
        print(solve(l,r)); putchar('
');
        update(l,r);
    } 
    return 0;
}

From:     HGOI

Name:ljc20020730

Date: 20181005

 

原文地址:https://www.cnblogs.com/ljc20020730/p/9745006.html