前言
完全挂的一场比赛,差点就爆零了.
本赛涉及题目:
A: [Curriculum Vitae]B: [Math Show]
D: [Monitor]
T1 Curriculum Vitae
题面
原题地址: [A]
题意
有n个数,不能改变数的顺序,问最少删除几个数使这个数串没有0在1的右边,输出这个数串的最大可能长度.
思路
我有两种思路,虽然有一种一直WA…(然而我看着没区别)
AC思路:
求出每个点左边0的个数和右边1的个数,求这两数和的最大值,再加上1就是答案(1是所枚的点)
代码
#include<bits/stdc++.h>
using namespace std;
int a[105],i,j,n,ans=1,ans_;
int main()
{
for(scanf("%d",&n),i=0;i<n;i++) scanf("%d",&a[i]);
for(i=0;i<n;i++)
{
ans_=0;
for(j=0;j<=i;j++) if(a[j]==0) ans_++;
for(j=i;j<n;j++) if(a[j]==1) ans_++;
ans=max(ans,ans_);
}
printf("%d",ans);
return 0;
}
小结
为什么我的第一个思路不行!!!(还是不要贴出来吧…)
T2 Math Show
题面
原题地址: [B]
题意
有n个任务,每个任务都有k个子任务,且每个任务的子任务是一样的,每个子任务完成后都获得一个点数,一个大任务完成额外获得一个点数,求在M时间里最多获得的点数.
思路
由于数据范围比较小,可以爆枚完成大任务的个数,然后求点数的最大值,具体见代码.
代码
#include<bits/stdc++.h>
using namespace std;
#define N 50
long long n,k,M,ans,tot,now,ans_,d,i,j,e;
int t[N+5];
int main()
{
for(scanf("%d%d%d",&n,&k,&M),i=0;i<k;i++) scanf("%d",&t[i]),tot+=t[i];//求完成一个大任务所需的时间;
sort(t,t+k);//初始化;
for(e=0;e<=n;e++)
{
now=tot*e;//完成e个大任务所需的时间;
if(now>M) break;//此时时间超限,以后也必定超限,故退出;
ans_ = (k+1)*e;//此时所获得的点数(大任务);
for(i=0;i<k;i++)
{
for(j=e+1;j<=n;j++)//将余下的时间分配到小任务里;
{
now+=t[i];
ans_++;
if(now>M)
{
ans_--;//最后使时间超限的点数去掉;
i=k+1;
break;
}
}
}
ans=max(ans,ans_);//求最大值;
}
printf("%d",ans);
return 0;
}
小结
扫一眼题面,我还以为这题不可做,然后就没写,没想到怎么水.
T3 Four Segments
题面
原题地址: [C]
题意
定义sum(x,y)为区间[a[x],a[y])的数字和,求三个点使sum(0,d1)-sum(d1,d2)+sum(d2,d3)-sum(d3,n) max. a数组是从零开始存数.
思路
求出前缀和的变式(a[i]存sum(0,i)),通过计算我们发现其实这个变式就是前缀和往后移以为,开头补0,这样可以用
for(i=1;i<=n;i++) scanf("%d",&a[i]),a[i]+=a[i-1]
来算前缀和,此时的sum(x,y)=a[y]-a[x];
原式=2*(a[d3]-a[d2]+a[d1])-a[n];
要使原式最小,只要使a[d3]-a[d2]+a[d1]最小,此时可以爆枚,但因为数据范围,n^3会TLE,于是又要加一步优化:
枚举d2的值,d3就是[a[d2],a[n]]的最大值的下标,d1就是[a[0],a[d2]]的最大值的下标,此时时间复杂度O(n^2).
代码#n^2
#include<bits/stdc++.h>
#define inf 0x7f7f7f7f
using namespace std;
long long ans=-inf,n,i,j,k,t[5007],d1,d2,d3,d1_,d2_,d3_,x,ans1,ans2,ans3;
int main()
{
for(scanf("%lld",&n),i=1;i<=n;i++)
{
scanf("%lld",&x);
t[i]=x+t[i-1];
}
for(d2_=0;d2_<=n;d2_++)
{
d2=d2_;d1=d3=d2;
for(d1_=0;d1_<=d2_;d1_++) if(t[d1_]>=t[d1]) d1=d1_;
for(d3_=d2_;d3_<=n;d3_++) if(t[d3_]>=t[d3]) d3=d3_;
if(t[d1]+t[d3]-t[d2]>=ans)
{
ans=t[d1]+t[d3]-t[d2];
ans1=d1;
ans2=d2;
ans3=d3;
}
}
printf("%lld %lld %lld",ans1,ans2,ans3);
return 0;
}
核心代码#n^3
for(d1_=0;d1_<=n;d1_++)
{
for(d2_=d1_;d2_<=n;d2_++)
{
for(d3_=d2_;d3_<=n;d3_++)
{
if(t[d1_]+t[d3_]-t[d2_]>=ans)
{
ans=t[d1_]+t[d3_]-t[d2_];
d1=d1_;d2=d2_;d3=d3_;
}
}
}
}
小结
重点是n^2的优化难想,我考完了才想到这种优化.
T4 Monitor
题面
原题地址: [D]
题意
有一个n*m的方阵,有些点会在某个时间坏掉,当坏掉的点组成一个k*k的方阵时,这个大方阵就变坏了,输出最小方阵变坏的时间,如果不可能变坏,输出-1。
思路
先将时间排序一下,每次更新ans,并将一个点置成坏,然后算一下有没有k*k的坏点。
如果爆枚k*k的方阵,但时间复杂度为q*n*m*k*k,直接爆
此时可以优化成每个点存其左上角k*k个点中坏掉的点的个数,当这个点到达k*k时,这个方阵就坏掉了。
此时每个点坏掉之后,向左下扩展一个k*k的方阵。
但这样时间复杂度为n^3*m,还是会TLE。
所以用另一种方式(抄的)(原理不会讲):
代码
#include<cstdio>
#include<algorithm>
using namespace std;
struct Point
{
int x,y,t;
bool operator<(const Point& b) const
{
return t<b.t;
}
}p[255000];
int b[510][510];
bool c[510][510];
int n,m,k,q;
bool judge(int x,int y)
{
int i,ans=0;
for(i=x;i<=n;i++)
{
if(b[i][y]<k) break;
ans++;
if(ans>=k) break;
}
for(i=x-1;i>=1;i--)
{
if(b[i][y]<k) break;
ans++;
if(ans>=k) break;
}
if(ans>=k) return true;
else return false;
}
int main()
{
int i,j;
bool boo;
scanf("%d%d%d%d",&n,&m,&k,&q);
for(i=1;i<=q;i++)
scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].t);
sort(p+1,p+q+1);
for(i=1;i<=q;i++)
{
int& x=p[i].x;
int& y=p[i].y;
c[x][y]=true;
for(j=y;j>=1;j--)
{
if(!c[x][j]) break;
b[x][j]=b[x][j+1]+1;
if(b[x][j]>=k)
{
boo=judge(x,j);
if(boo==true)
{
printf("%d",p[i].t);
return 0;
}
}
}
}
printf("-1");
return 0;
}
小结
其实还可以打二分,只是代码太长,懒得打。
T5 Chemistry in Berland
题面
原题地址: [E]
题意
有n种化学药品,拥有b1~bn,需要a1~an,有n-1条化学方程,第i个xi,ki代表ki个xi药品可以换成i+1药品,但1个i+1药品只能换成1个xi药品。
(此处的xi,i+1都是药品编号,i从1开始计数)
思路
刚开始是想建一棵数,从每个叶子节点一路换到树顶,再判断树顶,然后在第6个点WA了,因为是第6个点,所以我以为我代码错了,调了半天没调出来,于是又换了一种思路。
题目中有个条件1 ≤ x(j + 1 )≤ j,表示第i种药品只能转化成1~i-1种药品,于是就可以从尾部开始扫,再判断头部,此时还是WA6,看来不是我代码错了,再看一下数据范围,发现连long long 都能爆,所以改成double,然后就完美了。
代码AC
#include<bits/stdc++.h>
#define N 100000
#define LL long long
using namespace std;
pair<LL,LL> change[N+5];
LL x,n,i;
double need[N+5];
int main()
{
for(scanf("%lld",&n),i=1;i<=n;i++) scanf("%lf",&need[i]);
for(i=1;i<=n;i++) scanf("%lld",&x),need[i]=x-need[i];
for(i=2;i<=n;i++) scanf("%lld%lld",&change[i].first,&change[i].second);
for(i=n;i>1;need[i]=0,i--) need[change[i].first]+=need[i]>0?1LL*need[i]*change[i].second:need[i];
need[1]>0?printf("NO"):printf("YES");
return 0;
}
代码 树
//一直卡23点,不知道为什么,求各位dalao看看;
#include<bits/stdc++.h>
#define N 100000
#define LL long long
using namespace std;
struct Line
{
int v,k;
}line[N+5];
long long head[N+5],away[N+5]/*入度计算*/,e,n,i,j,x,k;
double tree[N+5],r;
void add(int u,int v,int k)
{
line[e].v=v;
line[e].k=k;
head[u]=e;
e++;
away[v]++;
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%lld",&n);
for(i=1;i<=n;i++) scanf("%lf",&tree[i]);
for(i=1;i<=n;i++) scanf("%lf",&r),tree[i]-=r;
for(i=2;i<=n;i++)
{
scanf("%lld%lld",&x,&k);
add(i,x,k);//叶子指向根;
}
for(i=1;i<=n;i++)
{
if(!away[i])//判断叶子节点;
{
x=i;
while(x!=1)//往根走;
{
if(tree[x]>=0) tree[line[head[x]].v]+=tree[x];
else tree[line[head[x]].v]+=tree[x]*line[head[x]].k*1LL;
x=line[head[x]].v;
}
}
}
tree[x]>=0?printf("YES"):printf("NO");
return 0;
}
小结
为什么第六个点就爆long long 了,出题人也太坑了吧,我还以为我代码错了呢!!!
T6 Random Query
题面
原题地址: [F]
题意
不会讲。
思路
只用计算的贡献
代码
#include<bits/stdc++.h>
const int N=1e6;
using namespace std;
int i,head[N+5];
long long ans;
int main()
{
for(scanf("%d",&n),i=1;i<=n;i++)
{
scanf("%d",&x);ans+=(long long)(i-head[x])*(n-i+1)*2;head[x]=i;
}
ans-=n;
printf("%.12lf",(double)ans/n/n);
return 0;
}
小结
代码抄的~~
总结
e~~~,想法都是有的,代码都是WA的。。。