NOIP2011复赛DAY2

12 篇文章 0 订阅
6 篇文章 0 订阅

NOIP2011——DAY2

1、计算系数

【题目分析】
相信看到这道题的时候,大家都有那么一丝丝的莫名的心慌(可怜的大家),作为第一题,他为什么不做好自己送分的职责,反而又要注意去模,又要注意long long。
相信只要略微有一点数学底子的同学知道,这其实就是杨辉三角(或组合数),对一个oier用dp的思想就可以解决了。(不知道杨辉三角的,你dp怎么学的…)

【代码】

#define M 1005
#include<cstdio>
#define mod 10007
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int dp[M][M];
int main(){
    int i,j,a,b,k,n,m;
    scanf("%d %d %d %d %d",&a,&b,&k,&n,&m);
    for(i=1;i<=k;i++)dp[1][i]=1;
    for(i=1;i<=k;i++)dp[i][1]=1;
    for(i=2;i<=k;i++){
        for(j=2;j<=k;j++)dp[i][j]=(dp[i-1][j]+dp[i][j-1])%mod;
    }
    int z=dp[n+1][m+1];
    for(i=1;i<=n;i++)z=(1ll*z*a)%mod;
    for(i=1;i<=m;i++)z=(1ll*z*b)%mod;
    printf("%d\n",z);
    return 0;
}

2、聪明的质监员

【题目分析】
看到最小值,自然就往二分答案上想了。若取得参数W小,则检验结果必然大,反之,检验结果必然小,这是具有明显的单调性的。他让我们求最小的绝对值,那就求呗。
又是一道要注意long long的题目,不开心。

【代码】

#define M 200005
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
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));
}
ll abss(ll x){return x>0?x:-x;}
int w[M],v[M],l[M],r[M],ge[M];
ll s,sum[M];
int n,m;
ll chk(int W){
    for(int i=1;i<=n;i++){
        if(w[i]>=W)ge[i]=ge[i-1]+1,sum[i]=sum[i-1]+v[i];
        else ge[i]=ge[i-1],sum[i]=sum[i-1];
    }
    ll an=0;
    for(int i=1;i<=m;i++){
        int g=ge[r[i]]-ge[l[i]-1];
        ll SUM=sum[r[i]]-sum[l[i]-1];
        an+=1ll*g*SUM;
    }return an;
}
int main(){
    cin>>n>>m>>s;
    for(int i=1;i<=n;i++)Rd(w[i]),Rd(v[i]);
    for(int i=1;i<=m;i++)Rd(l[i]),Rd(r[i]);
    int L=0,R=100000;
    while(R-L>1){
        int md=L+R>>1;
        if(chk(md)>s)L=md;
        else R=md;
    }cout<<min(abss(chk(L)-s),abss(chk(R)-s))<<endl;
    return 0;
}

3、观光公交

【题目分析】
一看题目的数据范围,就觉得是一道dp题,然后就在考虑状态如何转移,然后就……..放弃了,这TM根本就不是一道dp题,这一次使用加速器会对接下来的一段区间造成影响,但我怎么知道影响到哪里!
清空之前的思路,我们从原始的思路开始想,枚举。枚举这个加速器在哪一段使用,这样会对最终的答案造成什么影响,影响有多大。我们发现,车的出发时间与自身到达的时间、这个站点来的最晚的那个人的时间有关。如果给这一段无限加速,对答案的影响其实并不大,所以,贪心的思路就有了。
至于为什么是对的,其实已经很清楚了。对这个区间加速,他影响答案会因为这个站点来的最晚的那个人的时间所“打断”,贡献是有限的,用一句哲理性的话来总结:
“我的整个生命和全部精力,都已经献给世界上最壮丽的事业——为人类的解放而斗争。”
我们要用有限的加速器创造更大的影响。
既然这样,60分就到手了。

【60分代码】

#define M 1005
#define oo 1e16
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int n,m,k,p;
int D[M],S[10*M],T[10*M],tim[10*M];
ll ans=oo,an=oo;
vector<int>shang[M],xia[M];
int shangmx[M];
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));
}
void check(int w){
    ll res=0;
    int T=0,i,j;
    for(i=1;i<=n;i++){
        for(j=0;j<xia[i].size();j++){
            int q=xia[i][j];
            res+=T-tim[q];
        }if(T<shangmx[i])T=shangmx[i];
        T+=D[i];
    }
    if(res<an)an=res,p=w;
}
int main(){
    int i,j;
    Rd(n),Rd(m),Rd(k);
    for(i=1;i<n;i++)Rd(D[i]);
    for(i=1;i<=m;i++){
        Rd(tim[i]),Rd(S[i]),Rd(T[i]);
        shang[S[i]].push_back(i);
        xia[T[i]].push_back(i);
        shangmx[S[i]]=max(tim[i],shangmx[S[i]]);
    }
    check(0);
    ans=an;
    while(k--){
        an=oo;
        for(i=1;i<n;i++){
            if(!D[i])continue;
            D[i]--;
            check(i);
            D[i]++;
        }
        if(an>ans)break;
        ans=an;
        D[p]--;
    }cout<<ans<<endl;
    return 0;
}

上面的代码的复杂度是O(k*n^2),k是少不了了,就是查询太慢了。不过在赛场上写出来的话,其实已经很不错了,只要前面的水题不要爆,分数已经是很理想了。
至于100分到底怎么写,这是一个问题。
我们通过看上述暴力的代码,知道了如果在这个区间使用加速器,会影响到接下来的区间,而对之前的区间并没有影响。根据我们伟大的贪心,优化原本是m的询问吧。

【代码】

#define M 1005
#include<cstdio>
#include<iostream>
using namespace std;
int t[M*10],fr[M*10],to[M*10];
int d[M],ge[M],last[M],ar[M];
long long ans;
int n,m,k;
int main(){
    int i,j;
    scanf("%d %d %d",&n,&m,&k);
    for(i=1;i<n;i++)scanf("%d",&d[i]);
    for(i=1;i<=m;i++){
        scanf("%d %d %d",&t[i],&fr[i],&to[i]);
        last[fr[i]]=max(last[fr[i]],t[i]);
        ge[to[i]]++;
    }ar[1]=last[1];
    while(k--){
        for(i=2;i<=n;i++)ar[i]=max(ar[i-1],last[i-1])+d[i-1];
        int pos=0,mx=0;
        for(i=1;i<n;i++){
            if(!d[i])continue;
            int temp=0;
            for(j=i+1;j<=n;j++){
                temp+=ge[j];
                if(ar[j]<=last[j])break;
            }
            if(temp>mx)mx=temp,pos=i;
        }
        if(!pos)break;
        d[pos]--;
    }
    for(i=2;i<=n;i++)ar[i]=max(ar[i-1],last[i-1])+d[i-1];
    for(i=1;i<=m;i++)ans+=ar[to[i]]-t[i];
    cout<<ans<<endl;
    return 0;
}

【思路】
1、仅存的数学直觉。
2、二分答案。
3、dp->枚举->贪心。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值