您的位置:首页 > 其它

NOIP2011提高组Day2

2017-10-18 09:20 169 查看
NOIP2011提高组Day2

一、计算系数

数据:对于50%,a=b=1

对于100%,k∈[0,1000], n,m∈[0,k],且n+m=k,a,b∈[1,1e6]

a=b=1时就是杨辉三角。k又很小,n^2即可

a,b不为1时,可以打个表,发现a,b的次方就是所给的n,m,那就可以套个快速幂,再用上上面的那个杨辉三角就行。

我不是这样写的,我打了个表去算,也就只是n^2的事。

二、聪明的质检员

对于 30%的数据,n,m∈[1,500]

对于 100%的数据,有n,m∈[1,2e5],w,v∈[0,1e6],S∈[0,1e12],Li<=Ri∈[1,n]

分成两个问题,一个是求Y,一个是减少枚举量

对于求Y,把递推式展开后发现,求得都是区间里的,那么就用了一遍前缀和记录区间里的满足条件的个数,然后再用一遍刷漆,把每个点会被计算到的次数处理出来,最后再扫一遍就行了。这个是n。

减少枚举量就用二分就可以了,二分W,套用上面那个求出一个Y,当Y>S时,那就把R减小,当Y

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,m;
struct node{int l,r;}A[200005];
struct node1{int w,v;}A1[200005];
ll cnt[200005],sum[200005],S;
ll check(int W){
memset(cnt,0,sizeof cnt);
memset(sum,0,sizeof sum);
for(int i=1;i<=n;i++){//前缀和
cnt[i]=cnt[i-1];
if(A1[i].w>=W)cnt[i]++;
}
for(int i=1;i<=m;i++){//把符合条件的个数记录一下
sum[A[i].l]+=cnt[A[i].r]-cnt[A[i].l-1];
sum[A[i].r+1]-=cnt[A[i].r]-cnt[A[i].l-1];
}
ll k=0,res=0;//类似于刷漆
for(int i=1;i<=n;i++){
k+=sum[i];
if(A1[i].w>=W)res+=k*A1[i].v;//再加起来
}
return res;
}
int main(){
int mx=0,mn=1e9;
scanf("%d%d%lld",&n,&m,&S);
for(int i=1;i<=n;i++){
scanf("%d%d",&A1[i].w,&A1[i].v);
if(mx<A1[i].w)mx=A1[i].w;
if(mn>A1[i].w)mn=A1[i].w;
}
for(int i=1;i<=m;i++)scanf("%d%d",&A[i].l,&A[i].r);
int L=mn,R=mx;
ll ans=S;
while(L<=R){//二分
int mid=(L+R)/2;
ll res=check(mid);
if(res>S)L=mid+1;
else R=mid-1;
res=abs(res-S);/一直更新
if(ans>res)ans=res;
}
printf("%lld\n",ans);
return 0;
}


三、观光公交

数据:对于 10%,k=0

对于 20%,k=1

对于 100%的数据,n∈[1, 1e3],m∈[1,1e4],k∈[1,1e5],Di∈[1,100], Ti∈[1,1e5]

k=0是就一遍求过去,k=1时把所有路的时间都减1分别枚举。

正解之前要先懂得路径的减少会如何减少乘客的时间。

第一、减少一个路径的时间,会是到达的时间变早,但是乘客有一个到达的时间,就算车到了,车也要等,也就是人要等,来的再早也要等。那么就要尽量做到车道人到,这样最节省时间。

第二、减少一个路径的时间,会影响这条路径后面的部分点。比如这个点到下个点的时间大于下一个点的人的最晚到达时间,那下一个点的人要等。这个点的路径减少一,那么下一个点的人等的时间就会减少,那此时减少的乘客时间就不只是这个点的乘客,还有下一个点的等的乘客。因此一个点减少时间会影响到后面,那算的时候就要反着循环,先算后面,后面不会影响前面。

第三、贪心的想法,每次影响人数最多的那条路减一,肯定可以最大化的减少乘客的时间。

一个问题:不能先排序然后直接把一条路减减完吗?

我的想法是:不行。考试的时候我就这样写,要么我写错了,要么他本来就是不行的。因为每条边会影响到后面的点,那么把这个点减了后,如果后面的乘客要等了怎么办,等于没有减。那就浪费了。当前的最优状态和减一后的最优状态是不一样的

差不多了,最后一个我想不通的是,复杂度是NK的吧,不是1e9了吗??……

Code:

#include<bits/stdc++.h>
using namespace std;
int T[1005],D[1005],sum[1005],A[1005],B[1005];
int main(){
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<n;i++)scanf("%d",&T[i]);
int ans=0;
for(int i=1;i<=m;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
D[y]=max(D[y],x);//记录最晚到的每个点的时间
sum[z]++;//记录在每个点下车的人数
ans-=x;//计算最后的时间要把每个人的到达时间减去,读入的时候直接先减了
}
while(k--){
memset(A,0,sizeof A);
for(int i=2;i<=n;i++)
B[i]=max(B[i-1],D[i-1])+T[i-1];//在此状态下每个点的发车时间
for(int i=n;i>=2;i--){//A是这个点会影响到的人数
if(T[i-1]){
A[i-1]=sum[i];
if(B[i]>D[i])A[i-1]+=A[i];//如果后面的人要等,那么这里减一也会影响后面,一并加过来
}else A[i-1]=0;//一个人也无法影响,即减一也要等
}
int mx=0,x=-1;
for(int i=1;i<n;i++)
if(A[i]>mx)mx=A[i],x=i;//贪心求一个最大的
if(x==-1)break;//不能再有就退出,这应该是复杂度玄学的原因
T[x]--;
}
for(int i=2;i<=n;i++){//最后再求一遍
B[i]=max(B[i-1],D[i-1])+T[i-1];
ans+=B[i]*sum[i];
}
printf("%d\n",ans);
return 0;
}


说实话三道题确实不难。第二题正解考试时想到了。However,切分切错了…………第三题玄学复杂度让我想到了排个序全部剪掉,然后没分,只有k=1和k=0的30分。

加上以前考的NOIP2011Day1,应该不错吧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: