noip模拟题11.5

T1 大天使之剑

【问题描述】

小A在游戏⾥打怪。有⼀次,他⼀下⼦遇到了n个怪物。
每个怪物有一个生命值,第i个怪物的生命值是h_i。而小A除了生命值之外,还有一个属性是魔法值m。
小A和怪物们依次行动。每一回合,小A先行动,然后怪物们同时行动。
小A每次可以选择以下行动之一:
•普通攻击:令某个怪物的生命值减少1。
•重击:消耗1魔法值,令某个怪物的生命值减少2。
•群体攻击:消耗1魔法值,令全体怪物的生命值减少1。
而每个存活的怪物(生命值严格大于0)每次会令小A的生命值减少1。
假设小A有足够的生命值来维持存活,小A想知道自己至少需要被消耗多少生命值。

【输入文件】

输入文件为zhijian.in。
第一行为两个数n和m。
第二行为n个整数,第i个数为h_i。

【输出文件】

输出文件为zhijian.out。
输出一个整数,即小A至少被消耗的生命值。

【输入样例1】

2 1
2 1

【输出样例1】

1

【输入样例2】

3 4
2 4 4

【输出样例2】

6

【数据规模和约定】

对于20%的数据,m≤0;
对于30%的数据,m≤1;
对于50%的数据,m≤18;
存在30%的数据,n≤50,h_i≤50;
m=0与m=18各存在1个测试点,n≤1000,h_i≤1000;
对于100%的数据,1≤n≤100,000,0≤m≤100,0<h_i≤100,000。

一眼贪心无疑,问题是该怎么贪。
当还有魔法值时肯定是先群攻或重击。举些例子后可以很轻松地发现:当剩下的怪物大于2个时或当前怪物生命值为1肯定是群攻,否则为重击;魔法值为0时直接模拟即可(也可以不模拟,用个类似前缀和的形式直接算)。
代码(讨论了很多,可能有点丑):

#include
#include
#include
#include
#include
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
long long h[100005],num[100005],judge[100005],cnt[100005],bb[100005];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int main()
{
    freopen("zhijian.in","r",stdin);
    freopen("zhijian.out","w",stdout);
    int n=read(),m=read(),tot=0;
    long long ans=0,opp=0;
    for(int i=1;i<=n;i++)
    {
        scanf(AUTO,&h[i]);
        num[h[i]]++;
        if(!judge[h[i]])
        {
            judge[h[i]]=1;
            cnt[++tot]=h[i];
        }
    }
    sort(cnt+1,cnt+tot+1);
    int left=n;
    while(m>0)
    {
        if(left>2)
        {
            m--;
            ans+=left-num[1];
            left-=num[1];
            if(num[1]>0)
            {
                num[1]=0;
                cnt[1]=cnt[2]-1;
                for(int i=2;i<=tot;i++)
                {
                    if(num[cnt[i]]>0)
                    {
                        num[cnt[i]-1]=num[cnt[i]];
                        num[cnt[i]]=0;
                        if(i!=tot)
                            cnt[i]=cnt[i+1]-1;
                    }
                }
                cnt[tot]=0;
                tot--;
            }
            else
            {
                for(int i=1;i<=tot;i++)
                {
                    if(num[cnt[i]]>0)
                    {
                        num[cnt[i]-1]+=num[cnt[i]];
                        num[cnt[i]]=0;
                        cnt[i]-=1;;
                    }
                }
            }
        }
        else
        {
            if(left==2)
            {
                if(num[1]==1)
                {
                    num[1]=0;
                    m--;ans+=1;left--;
                    cnt[1]=cnt[2]-1;
                    num[cnt[2]-1]=num[cnt[2]];
                    num[cnt[2]]=0;
                    cnt[tot]=0;
                    tot--;
                }
                else if(num[1]==2)
                {
                    printf(AUTO,ans);
                    return 0;
                }
                else if(num[2]>0)
                {
                    m--;ans+=1;
                    num[2]--;
                    left--;
                    cnt[1]=cnt[2];
                    tot--;
                    cnt[2]=0;
                }
                else
                {
                    m--;
                    ans+=2;
                    num[cnt[1]]--;
                    num[cnt[1]-2]++;
                    cnt[1]-=2;
                }
            }
            else if(left==1)
            {
                if(num[1]==1||num[2]==1)
                {
                    printf(AUTO,ans);
                    return 0;
                }
                else
                {
                    m--;
                    ans+=1;
                    num[cnt[1]-2]=num[cnt[1]];
                    num[cnt[1]]=0;
                    cnt[1]-=2;
                }
            }
        }
    }
    for(int i=1;i<=tot;i++)
    {
        while(num[cnt[i]]>0)
        {
            bb[1+opp]=bb[opp]+cnt[i];
            ans+=bb[opp+1];
            opp++;
            num[cnt[i]]--;
        }
    }
    printf(AUTO,ans-opp);
    return 0;
}

T2 保卫萝卜

【问题描述】

小A喜欢种萝卜,他买了⼀块(10^10+1)×(10^10+1)平方米的地。每块地都是1平方米。
小A知道小B要来偷他的萝卜。为了对抗他,小A决定在一些萝卜地里画咒符。
小A来到了萝卜地的中央,然后在这个地方画了一个咒符。现在他要边走边画更多的咒符。每次行动,小A可以向上、下、左、右走某个整数米的距离。小A边走边会在途径的格⼦留下咒符。
当小A画完了以后,他把他的路线记在了纸上。现在他想知道一共有多少平方米的萝卜不会被小B占领。
小B是这样占领萝卜的:首先小B会占领边界的某块萝卜地,然后,如果有某块未被占领的萝卜地与已被占领的萝卜地有公共边,并且没有被小A画上咒符,那么它也会被占领。
帮小A算算他能保护多少萝卜吧!

【输入文件】

输入文件为luobo.in。
第一行为一个整数n,即小A行动的次数。
接下来n行,每行格式为d x,其中d是一个UDLR之中的字符(分别代表上下左右),x代表小A这次走的米数。

【输出文件】

输出文件为luobo.out。
输出一个整数,即小A能保护的萝卜个数。

【输入样例1】

5
R 8
U 9
L 9
D 8
L 2

【输出样例1】

101

【输入样例2】

7
R 10
D 2
L 7
U 9
D 2
R 3
D 10

【输出样例2】

52

【数据规模和约定】

对于30%的数据,所有的x=1。
对于60%的数据,所有的x≤2。
对于100%的数据,n≤1,000,x≤10^6。

考试时写了个60分的bfs,搜外面,结果只得了30。答案对的,前几组T了。
正解要离散化。不会,没写。
30分:

#include
#include
#include
#include
#include
using namespace std;
struct node{
    int x,y;
};
queueq;
int xx[4]={0,0,1,-1},yy[4]={1,-1,0,0};
int map[3002][3002],tot;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void bfs(int x,int y)
{
    node o;o.x=x;o.y=y;
    q.push(o);
    map[x][y]=1;tot++;
    while(!q.empty())
    {
        node p=q.front();
        q.pop();
        for(int i=0;i<=3;i++)
        {
            int tmpx=p.x+xx[i],tmpy=p.y+yy[i];
            if(tmpx>=1&&tmpx<=3000&&tmpy>=1&&tmpy<=3000&&0==map[tmpx][tmpy])
            {
                node l;l.x=tmpx;l.y=tmpy;
                map[tmpx][tmpy]=1;tot++;
                q.push(l);
            }
        }
    }
}
int main()
{
    freopen("luobo.in","r",stdin);
    freopen("luobo.out","w",stdout);
    int k=read();
    int nowx=1500,nowy=1500;
    map[nowx][nowy]=1;
    while(k--)
    {
        char c;
        scanf("%c",&c);
        int x=read();
        if(c=='R')
        {
            for(int i=nowy+1;i<=nowy+x;i++)
                map[nowx][i]=1;
            nowy+=x;
        }
        else if(c=='L')
        {
            for(int i=nowy-1;i>=nowy-x;i--)
                map[nowx][i]=1;
            nowy-=x;
        }
        else if(c=='U')
        {
            for(int i=nowx-1;i>=nowx-x;i--)
                map[i][nowy]=1;
            nowx-=x;
        }
        else if(c=='D')
        {
            for(int i=nowx+1;i<=nowx+x;i++)
                map[i][nowy]=1;
            nowx+=x;
        }
    }
    for(int i=1;i<=3000;i++)
        if(!map[1][i])
        {
            bfs(1,i);
            break;
        }
    printf("%d",3000*3000-tot);
    return 0;
}

标程:

#define PROC "luobo"
#include
#include
#include
using namespace std;
typedef long long qw;
#ifdef WIN32
#define lld "%I64d"
#else
#define lld "%lld"
#endif
#define _l (qw)
#define mkword(x,y) (((x)<<16)|(y))
#define hiword(x) ((x)>>16)
#define loword(x) ((x)&0x0000FFFF)
struct point {
    int x,y,d;
};
const int maxn=3009;
const int mov[4][2]={{-1, 0},{1, 0},{0, -1},{0, 1}};
inline int getDir(char x) {
    if(x=='U')
        return 0;
    else if(x=='D')
        return 1;
    else if (x=='L')
        return 2;
    else if (x=='R')
        return 3;
    else
        return -1;
}
int n,dx[maxn],dy[maxn],rx[maxn],ry[maxn],tv,tx,ty,cx,cy;
point p[maxn];
char a[maxn][maxn];
int dispNums(int* r,int tr,int* t)
{
    int n=1;
    sort(r,r+tr);
    t[0]=r[0];
    for(int i=1;i<tr;++i)
        if(r[i]^r[i-1])
            t[n++]=r[i];
    return n;
}
void bfs()
{
    static int q[maxn*maxn];
    q[0]=0;
    int hd=0,tl=1;
    a[0][0]=1;
    while(hdint x=hiword(q[hd]),y=loword(q[hd]);
        ++hd;
        for(int i=0;i<4;++i)
        {
            int x0=x+mov[i][0],y0=y+mov[i][1];
            if (x0>=0&&x0y0>=0&&y0x0][y0]==0)
            {
                a[x0][y0]=1;
                q[tl++]=mkword(x0,y0);
            }
        }
    }
}
int main()
{
    freopen(PROC".in","r",stdin);
    freopen(PROC".out","w",stdout);
    scanf("%d",&n);
    cx=0;
    cy=0;
    p[0].x=0;
    p[0].y=0;
    tv=0;
    for(int i=1;i<=n;++i)
    {
        char tmp[3];
        int dx;
        scanf("%s%d",tmp,&dx);
        p[i].d=getDir(tmp[0]);
        cx+=dx*mov[p[i].d][0];
        cy+=dx*mov[p[i].d][1];
        p[i].x=cx;
        p[i].y=cy;
    }
    for(int i=0;i<=n;++i)
        for(int j=-1;j<=1;++j)
        {
            rx[tv]=p[i].x+j;
            ry[tv]=p[i].y+j;
            ++tv;
        }
    tx=dispNums(rx,tv,dx);
    ty=dispNums(ry,tv,dy);
    for(int i=0;i<=n;++i)
    {
        p[i].x=lower_bound(dx,dx+tx,p[i].x)-dx;
        p[i].y=lower_bound(dy,dy+ty,p[i].y)-dy;
    }
    memset(a,0,sizeof(a));
    for(int i=0;ifor(int x=p[i].x;1;x+=mov[p[i+1].d][0])
        {
            for(int y=p[i].y;1;y+=mov[p[i+1].d][1])
            {
                a[x][y]=2;
                if(y==p[i+1].y)
                    break;
            }
            if(x==p[i+1].x)
                break;
        }
    bfs();
    qw ans=0;
    for(int x=0; xx)
        for (int y=0;yy)
            if(a[x][y]^1)
                ans+=(_l dy[y+1]-dy[y])*(dx[x+1]-dx[x]);
    printf(lld"\n",ans);
}

T3 打地鼠

【问题描述】

小A喜欢打地鼠!
小共有n只地鼠按照顺序出现,第i只地鼠的肥胖度为a[i]。每次小A可以打地鼠,但是要保证之前没打过任何地鼠,或这只地鼠比之前的任何一只小A打过的地鼠都要肥。
当然题目没有这么简单。如果小A没有打完所有地鼠,那么剩下的地鼠将会再次出现(来被小A打)。当然,地鼠不是傻子,如果自己出现了t次以后还没被打死,那么就不会再出现了。
现在问,小A最多能打多少地鼠?

【输入文件】

输入文件为dishu.in。
第一行包含4个整数k n max t。分别表示数据组数,地鼠个数,a[i]的最大值,每个地鼠出现的最大次数。
接下来k行,每行表示一组数据,包含n个整数,代表a[1]到a[n]。

【输出文件】

输出文件为dishu.out。
对于每组数据输出小A最多能打到的地鼠个数。

【输入样例】

3 3 5 2
3 2 1
1 2 3
2 3 1

【输出样例】

2
3
3

【数据规模和约定】

对于30%的数据,n*t≤1,000。
对于60%的数据,n*t≤100,000。
对于80%的数据,n*max≤100,000。
对于100%的数据,1≤k≤10,1≤n,max≤10^5,1≤t≤10^9,n*max≤20,000,000。

LIS吧,看出来很容易。求LIS有2种办法,一种O(n²),一种O(nlogn),考试时写了个n²的,过了5组。然而没想到数据水,nlogn的居然可以过完。。。
nlogn的实现要用到队列优化。
记一个数组g[i]表示序列长度为 i 的时候肥胖值的最小值,每次枚举 n 时用lower_bound寻找最后一个小于 h[i] 的 g,b[i] 就等于这个 g 的下标+1。
代码:

#include
#include
#include
#include
using namespace std;
int b[100005];
int h[100005],g[100005]; 
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int main()
{
    freopen("dishu.in","r",stdin);
    freopen("dishu.out","w",stdout);
    int K,n,maxnn,t;
    K=read();n=read();maxnn=read();t=read();
    while(K--)
    {
        int ans=0;
        memset(g,127,sizeof g);
        for(int i=1;i<=n;i++)
            h[i]=read();
        g[1]=h[1];
        g[0]=0;
        b[1]=1;
        for(int k=1;k<=t;k++)
            for(int i=1;i<=n;i++)
            {
                int k=lower_bound(g,g+n+1,h[i])-g-1;
                b[i]=k+1;
                g[k+1]=min(g[k+1],h[i]);
                ans=max(b[i],ans);
            }
        printf("%d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(历程,练习,模拟题)