T1 最大序列
100pts
维护一个栈
从前往后扫描,如果栈为空或当前数及之后数中的最大值大于栈顶的数,就将最大值之前的所有数入栈,并且把最大值保存在答案中。
否则将栈顶的元素弹出,并保存在答案中。
由于最大值从后往前是单调递增的,所以直接用一个数组维护就可以O(1)查询
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=200005;
int n,a[N],ans[N],f[N],tot,now;
stack<int>s;
int read()
{
int sum=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return sum*f;
}
void print(int x)
{
if(x<0)x=-x,putchar('-');
if(x>9)print(x/10);
putchar(x%10+'0');
}
int main()
{
// freopen("seq.in","r",stdin);
// freopen("seq.out","w",stdout);
n=read();
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=n;i>=1;i--)
{f[i]=max(f[i+1],a[i]);}
int i=1;
while(i<=n+1)
{
if(!s.size()||f[i]>s.top())
{
int now=f[i];
while(a[i]!=now){s.push(a[i]);i++;}
ans[++tot]=now;
i++;
}
else{
ans[++tot]=s.top();
s.pop();
}
}
for(int i=1;i<=n;i++)
{
print(ans[i]);
if(i!=n)putchar(' ');
}
return 0;
}
T2 斩杀计划
100pts
用一个桶
b
[
]
b[ ]
b[]统计所有攻击力的小弟个数。
从小到大枚举使用腐败药水的次数,把能拉过来的小弟都拉过来,当小弟的攻击力ans>=sum (血量总值)时就跳出。这样保证腐败药水的次数一定是最小的。
保证ans>=sum的前提下,删去小弟中多余的。
先删3 因为3的花费为4,显然最不划算
然后删2 因为2和1的花费相同,但2比1攻击力贡献多1
最后删1
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=5000005,maxn=30000;
int n,m,sum,x,ans,s1,s2,s3,tot;
int b[N];
int read()
{
int sum=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return sum*f;
}
void print(int x)
{
if(x<0)x=-x,putchar('-');
if(x>9)print(x/10);
putchar(x%10+'0');
}
bool comp(int a,int b){
return a>b;
}
int main()
{
// freopen("zhanshajihua.in","r",stdin);
// freopen("zhanshajihua.out","w",stdout);
n=read();sum=read();
for(int i=1;i<=n;i++)
{x=read();b[x]++;}
for(int i=1;i+2<=maxn;i+=3)
{
s1+=b[i];s2+=b[i+1];s3+=b[i+2];
ans=s1+s2*2+s3*3;
if(ans>=sum)break;
tot++;
}
if(ans<sum){printf("-1");return 0;}
while(ans-3>=sum&&s3){s3--;ans-=3;}
while(ans-1>=sum&&s1){s1--;ans-=1;}
while(ans-2>=sum&&s2){s2--;ans-=2;}
print(tot);putchar(' ');
print(s1+s2+s3*4+tot);
return 0;
}
T3 分离计划
100pts
先求出所有数的最大值ma,最小值mi。
然后二分枚举答案ans。
check()函数中把图分成A,B两部分:
若能保证A部分的所有数x,满足x>=ma-ans ;B部分的所有数y,满足y>=ans-mi,则当前ans合法。
否则把图旋转九十度,继续check,一直旋转到原来仍不合法,则当前ans不合法。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=2005,inf=0x3f3f3f3f;
int n,m,a[4][N][N],f[N][N],k=1,mi,ma;
int max(int x,int y){ return x>y?x:y;}
int min(int x,int y){ return x>y?y:x;}
int read()
{
int sum=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return sum*f;
}
void print(int x)
{
if(x<0)x=-x,putchar('-');
if(x>9)print(x/10);
putchar(x%10+'0');
}
void init(int x,int y)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
a[y][j][n-i+1]=a[x][i][j];
swap(n,m);
}
int judge(int now)
{
int y=m;
memset(f,0,sizeof(f));
for(int i=1;i<=n;i++)
for(int j=1;j<=y;j++)
{
if(ma-a[k][i][j]>now){y=j-1;break;}
else f[i][j]=1;
}
for(int i=n;i>=1;i--)
for(int j=m;j>=1;j--)
{
if(f[i][j])break;
if(a[k][i][j]-mi>now)return 0;
}
return 1;
}
void change()
{
k=(k+1)%4;swap(n,m);
}
int check(int now)
{
if(judge(now))return 1;change();
if(judge(now))return 1;change();
if(judge(now))return 1;change();
if(judge(now))return 1;change();
return 0;
}
int main()
{
// freopen("separate.in","r",stdin);
// freopen("separate.out","w",stdout);
n=read();m=read();
mi=inf;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
a[0][i][j]=read();
ma=max(ma,a[0][i][j]);
mi=min(mi,a[0][i][j]);
}
init(0,1);init(1,2);init(2,3);
int l=0,r=ma-mi;
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid))r=mid;
else l=mid+1;
}
print(l);
return 0;
}