# | 题目 |
---|---|
A | 【NOIP 2011 提高组 Day2】计算系数 |
B | 【NOIP 2011 提高组 Day2】聪明的质监员 |
C | 【NOIP 2011 提高组 Day2】观光公交 |
A. 【NOIP 2011 提高组 Day2】计算系数
考试时看到这题我就被吓住了,不会是数论吧?? 我数论菜的一塌糊涂,,
不管怎么第一题也不能爆零啊,打表也得给他打出来
于是就真的开始打表了,,,,,
规律!!我终于找到规律了!!!
f[k][i]代表k次方第i项的系数,可以得到状态转移方程:
f[k][i]=f[k-1][i-1]+f[k-1][i];
并且方程的系数是左右对称的!!!所以只用递推出左半边即可!
for(int i=3;i<=k;i++){
f[i][1]=1;
for(int j=2;j<=(i+1)/2;j++){
f[i][j]=(f[i-1][j-1]+f[i-1][j])%mod;
}
if(i&1)f[i][(i+1)/2+1]=f[i][(i+1)/2];
else f[i][(i+1)/2+1]=(f[i-1][(i+1)/2]+f[i-1][(i+1)/2+1])%mod;
}
至于还有个a,b,用快速幂算出来乘上就行了
B. 【NOIP 2011 提高组 Day2】聪明的质监员
一开始的检验值Y怎么算都搞了我好久,,,
不过问题不大,看到要使S-Y绝对值最小,emm,,,,二分就是你了!!
二分很好写,结果卡在了计算Y那里,直接每次遍历肯定要超时,,这咋整
emm,,,实在没想出来,最后用vector预处理了每个点包含的边(这和暴力枚举有什么区别啊喂!!!)
果然,,Memory Limit Exceeded???
好吧看来我的vector爆掉了
正解:二分,前缀和。
看完题解,妙啊!前缀和果然方便多了
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,m,L[200100],R[200100];
ll s,w[200100],v[200100],ans=1e9,prn[200100],prv[200100];
int work(int x){
for(int i=1;i<=n;i++){
if(w[i]>=x){
prn[i]=prn[i-1]+1;
prv[i]=prv[i-1]+v[i];
}
else{prn[i]=prn[i-1];prv[i]=prv[i-1];
}
}
ll sm=0;
for(int i=1;i<=m;i++){
int l=L[i],r=R[i];
ll s1=prn[r]-prn[l-1];
ll s2=prv[r]-prv[l-1];
sm+=s1*s2;
}
if(abs(sm-s)<abs(ans-s)) ans=sm;
if(sm>s)return 0;
return 1;
}
int main(){
// freopen("qc.in","r",stdin);
// freopen("qc.out","w",stdout);
scanf("%d%d%lld",&n,&m,&s);
for(int i=1;i<=n;i++)scanf("%lld%lld",&w[i],&v[i]);
for(int i=1;i<=m;i++){
scanf("%d%d",&L[i],&R[i]);
}
int l=0,r=1000001;
while(l<r){
int mid=(l+r)/2;
if(work(mid))r=mid;
else l=mid+1;
}
printf("%lld",abs(ans-s));
return 0;
}
C. 【NOIP 2011 提高组 Day2】观光公交
考试时就没打算写正解,,,
果断暴力枚举在每个站点使用氮气加速器,暴力30分到手~
正解:贪心,前缀和(怎么又是你)
last[i]表示第i个站点最晚到达的乘客
leave[i]表示从第一个站点到第i个站点总共有多少人下车,leave[r]-leave[l-1]就可以求出从l站点到r站点有多少人下车(前缀和真方便*2)
arrive[i]表示汽车到达第i个站点的时间
will[i]表示第i个站点使用加速器最远能影响到哪个站点
因为车到站时只有两种情况:
- 车等人,即last[i+1]>arrive[i+1],这种情况下使用加速器只会对下一站要下车的乘客有影响,因为你提前到了 这个站点也要等人,出发时间是一样的,所以will[i]=i+1
- 人等车,即last[i+1]<=arrive[i+1],这种情况下使用加速器就会对后面站点的乘客产生影响,至于影响多远,就要看下一个站点能影响多远,所以will[i]=will[i+1]
这样我们每次就找在哪个站点使用加速器影响最远就行啦
还有每次使用完加速器就要把这个站点之后的arrive数组更新哦
#include<bits/stdc++.h>
using namespace std;
int n,m,k,ne[1005],arrive[1005],leave[1005],last[1005],will[1005],T;
struct node{
int x,y,t;
}pas[10005];
void bus(){
for(int ll=1;ll<=k;ll++){
will[n-1]=n;
for(int i=n-2;i>=1;i--){
if(arrive[i+1]>last[i+1])will[i]=will[i+1];
else will[i]=i+1;
}
int ans=-1,op;
for(int i=1;i<n;i++){
if(leave[will[i]]-leave[i]>ans&&ne[i]){
ans=leave[will[i]]-leave[i];
op=i;
}
}
if(ans==-1)return;
T-=ans;
ne[op]--;
for(int i=op;i<=n;i++){
arrive[i]=max(arrive[i-1],last[i-1])+ne[i-1];
}
}
}
int main(){
// freopen("bus.in","r",stdin);
// freopen("bus.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<n;i++)scanf("%d",&ne[i]);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&pas[i].t,&pas[i].x,&pas[i].y);
last[pas[i].x]=max(pas[i].t,last[pas[i].x]);
leave[pas[i].y]++;
}
for(int i=2;i<=n;i++){
leave[i]+=leave[i-1];
arrive[i]=max(arrive[i-1],last[i-1])+ne[i-1];
}
for(int i=1;i<=m;i++){
T+=arrive[pas[i].y]-pas[i].t;
}
bus();
printf("%d",T);
return 0;
}