您的位置:首页 > 其它

NOIP2011复赛提高组day2(A:计算系数 B:聪明的质监员 C:观光公交)

2016-10-30 19:12 387 查看
A题:

联赛第一题,就是送分的,

这次是用组合数学送,

只要你学过二项式定理(有点水平的教练应该都会讲),

基本上是秒杀,

不过要注意的是mod

这题按理来说是不需要long long的,但是保险起见,还是加一个吧,又不会狗带

蒟蒻的lowest代码上线:

#include<bits/stdc++.h>
using namespace std;
#define M 1005
const int P=10007;
int Com[M][M];
int getCom(int n,int m){
if(Com
[m])return Com
[m];
return Com
[m]=(getCom(n-1,m)+getCom(n-1,m-1))%P;
}
int main(){
int a,b,k,n,m,ans=1;
scanf("%d%d%d%d%d",&a,&b,&k,&n,&m);
if(n+m!=k){
puts("0");
return 0;
}for(int i=1;i<=n;i++)ans=1ll*ans*a%P;
for(int i=1;i<=m;i++)ans=1ll*ans*b%P;
for(int i=0;i<=k;i++){Com[i][0]=1;Com[i][i]=1;}
ans=1ll*ans*getCom(k,n)%P;
printf("%d\n",ans);
return 0;
}


B题:

话说11年两天的第二题似乎都很水

基本上快和第一题一个水平了

这题显然满足二分的性质

于是就二分吧

二分之后就更水了

前缀和维护一下区间基本就结束了

(前两题我只敲了30+就好了)

然而考完试听到xiaoC说有人敲出了主席数、线段树、数状数组等等的时候

你们无法想象我当时的表情

简直惊为天人,(蒟蒻现在连主席数模板都不会敲0.0)

他们的脑洞实在有点大。。。

一个离线的区间维护问题,最简单的不就是前缀和么~~~

代码get:

#include<bits/stdc++.h>
using namespace std;
#define M 200005
#define LL long long
void Rd(int &res){
char c;res=0;
while(c=getchar(),!isdigit(c));
do res=(res<<3)+(res<<1)+(c^48);
while(c=getchar(),isdigit(c));
}
int n,m;
int wei[M],val[M],lef[M],rig[M],num[M];
LL sum[M];
LL check(int W){
LL res=0;
for(int i=1;i<=n;i++){
sum[i]=sum[i-1];//前缀和分别维护重量和个数
num[i]=num[i-1];
if(wei[i]>=W){
sum[i]+=val[i];
num[i]++;
}
}for(int i=1;i<=m;i++)res+=(sum[rig[i]]-sum[lef[i]-1])*(num[rig[i]]-num[lef[i]-1]);
return res;
}
int main(){
LL S;
cin>>n>>m>>S;
for(int i=1;i<=n;i++){Rd(wei[i]);Rd(val[i]);}
for(int i=1;i<=m;i++){Rd(lef[i]);Rd(rig[i]);}
int l=1,r=1e6;LL ans=2e15;
while(l<=r){
int mid=l+r>>1;LL res=check(mid);
if(abs(res-S)<ans)ans=abs(res-S);
if(res<S)r=mid-1;
else if(res>S)l=mid+1;
else break;
}cout<<ans<<endl;
return 0;
}


C题:

这题简直了!!

敲了150+只有30分,欲哭无泪

虽说博主当时本来就是奔着水分去的

(考试的时候想到了贪心,但是一时证明不了正确性、也没有想到什么好办法去模拟)

于是敲暴力。。

xiaoC大人告诉我们要学会切分,

于是我首先花了40+的时间敲了20%的数据:

对于10% 的数据,k=0 ;

对于20% 的数据,k=1 ;

。。。。。。。

也不知道我当时是怎么想的。

跟day1的情况几乎一模一样

那个Mayan 游戏我费了60+的时间敲30%数据(只有一行)

(调试了半天)

然而敲完了发现,那完全可以直接枚举。。

瞬间KO的

于是回归主题,

这题只要再深入思考一下,

就可以明确这是贪心

我们只要找到使用加速器后对结果影响最大的那条边就可以了

这个贪心这么证明其正确性呢

显然的是

在某条边使用了加速器之后

如果到达了之后的某个点时需要等人

加速器就对在之后的点下车的乘客不起丝毫效果

也就是说我们在使用了加速器改变了这条边的权值之后

对其他区域是不会有影响的

这些区域完全独立

于是我们可以直接枚举每一个加速器使用在那条边上

但是枚举这条边时显然是不能傻傻的一遍枚举过来的、

那样O(k*n^2)会T的

于是这个时候我们再仔细想想就会发现

对于某一个满足条件的区间来说

在它的左端点使用加速器显然要优于其他端点

于是这满足尺取法的性质

每次选完一个最大区间

就从区间的右端点开始重新贪心就行了

复杂度为O(n*k)

最后计算所需时间时

只要将下车总时间减去上车总时间就可以得出答案了

其实这一题在加上一个优先队列可以将复杂度进一步优化到O(n*logn)

但由于博主是蒟蒻,就不说明了

代码来袭:

#include<bits/stdc++.h>
using namespace std;
#define N 1010
void Rd(int &res){
char c;res=0;
while(c=getchar(),!isdigit(c));
do res=(res<<3)+(res<<1)+(c^48);
while(c=getchar(),isdigit(c));
}
int d
,ti
,cost
,Add
;
//d[x]代表x到x+1的距离
//ti[x]代表最后一个在点x上车的乘客的上车时间
//cost[x]代表在当前状态下,到达点x的时间
//Add[x]代表在点x下车的人数
int n,m,k;
void solve(){
while(k--){
int l=1,r=2,add,T=0,mx=0,id=-1;
for(int i=1;i<=n;i++){//在使用加速期之后cost的值将会改变
cost[i]=T+d[i-1];
T=max(cost[i],ti[i]);
}while(r<=n){
while(d[l]==0&&r<=n)l++,r++;//d不能小于0
if(r>n)break;
add=Add[r];
while(r<=n){
if(cost[r]<=ti[r])break;
//当汽车到达点i的时间小于等于最后一个乘客上车时间时,加速器对之后的点无影响
add+=Add[++r];
}if(add>mx){
mx=add;
id=l;
}l=r;r++;
}if(id!=-1)d[id]--;
else break;//无法再使用加速器了了
}
}
int main(){
int sum=0;
scanf("%d %d %d",&n,&m,&k);
if(n==1){puts("0");return 0;}
for(int i=1;i<n;i++)Rd(d[i]);
for(int i=1;i<=m;i++){
int t,a,b;
Rd(t);Rd(a);Rd(b);
sum+=t;
if(t>ti[a])ti[a]=t;
Add[b]++;
}solve();
int T=0;
for(int i=1;i<=n;i++){//重新计算到某一点的距离
cost[i]=T+d[i-1];
T=max(cost[i],ti[i]);
}int ans=0;
for(int i=1;i<=n;i++)ans+=Add[i]*cost[i];
cout<<ans-sum<<endl;
return 0;
}


于是两天相加450瞬间爆炸
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: