小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至少被消耗的生命值。
2 1
2 1
1
3 4
2 4 4
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;
}
小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能保护的萝卜个数。
5
R 8
U 9
L 9
D 8
L 2
101
7
R 10
D 2
L 7
U 9
D 2
R 3
D 10
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;
};
queue q;
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);
}
小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;
}