noip2011day2总结

1.计算系数
(factor.cpp/c/pas)
【问题描述】
给定一个多项式(ax + by)k,请求出多项式展开后xn ym项的系数。
【输入】
输入文件名为 factor.in。
共一行,包含 5 个整数,分别为a,b,k,n,m,每两个整数之间用一个空格隔开。
【输出】
输出文件名为 factor.out。
输出共 1 行,包含一个整数,表示所求的系数,这个系数可能很大,输出对10007 取
模后的结果。
【输入输出样例】
factor.in factor.out
1 1 3 1 2 3
【数据范围】
对于 30%的数据,有0≤k≤10;
对于 50%的数据,有a = 1,b = 1;
对于 100%的数据,有0≤k≤1,000,0≤n, m≤k,且n + m = k,0≤a,b≤1,000,000。
分析:送分题~可以用杨辉三角递推,也可以用组合公式直接用逆元求解,后者的适用范围更广一点

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
long long mod=10007;
long long fac[10005],aaa[10005],bbb[10005];
void exgcd(long long a,long long b,long long &x,long long &y,long long d)
{
    if(b==0)
    {
        d=a;
        x=1;
        y=0;
        return ;
    }
    long long x0,y0;
    exgcd(b,a%b,x0,y0,d);
    x=y0;
    y=x0-a/b*y0;
}
long long work(long long a)
{
    long long x,y,d;
    exgcd(a,mod,x,y,d);
    return (x%mod+mod)%mod;
}//扩展欧几里得求逆元 
int main()
{
    freopen("factor.in","r",stdin);
    freopen("factor.out","w",stdout);
    long long a,b,n,m,k;
    cin>>a>>b>>k>>n>>m;
    fac[0]=1;
    for(int i=1;i<=k;i++)
       fac[i]=fac[i-1]*i%mod;
    long long ans=fac[k]*work(fac[n])%mod*work(fac[k-n])%mod;   
    aaa[0]=1;
    for(int i=1;i<=n;i++)
       aaa[i]=aaa[i-1]*a%mod;
    bbb[0]=1;
    for(int i=1;i<=m;i++)
       bbb[i]=bbb[i-1]*b%mod;
    cout<<(ans*aaa[n]%mod*bbb[m]%mod);
    return 0;
}  

T2
描述
小 T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有n个矿石,从1到n逐一编号,每个矿石都有自己的重量wi以及价值vi。检验矿产的流程是:
1、给定m个区间[Li,Ri];
2、选出一个参数W;
3、对于一个区间[Li,Ri],计算矿石在这个区间上的检验值Yi:
Yi=(∑j1)∗(∑jvj) , j∈[Li,Ri]且wj≥WYi=(∑j1)∗(∑jvj) , j∈[Li,Ri]且wj≥W
j是矿石编号

这批矿产的检验结果Y 为各个区间的检验值之和。即:Y=∑i=1mYiY=∑i=1mYi
若这批矿产的检验结果与所给标准值S相差太多,就需要再去检验另一批矿产。小T不想费时间去检验另一批矿产,所以他想通过调整参数W的值,让检验结果尽可能的靠近标准值S,即使得S-Y的绝对值最小。请你帮忙求出这个最小值。

格式
输入格式

第一行包含三个整数n,m,S,分别表示矿石的个数、区间的个数和标准值。

接下来的n行,每行2个整数,中间用空格隔开,第i+1行表示i号矿石的重量wi和价值vi 。

接下来的m行,表示区间,每行2个整数,中间用空格隔开,第i+n+1行表示区间[Li,Ri]的两个端点Li和Ri。注意:不同区间可能重合或相互重叠。

输出格式

输出只有一行,包含一个整数,表示所求的最小值。
样例输入1
5 3 15
1 5
2 5
3 5
4 5
5 5
1 5
2 4
3 3
样例输出1
10

分析:题意很简单,二分的话应该不是难点,唯一难一点的就是check的方式上,需要用前缀和的思想,当然这也只能适用于这种没有修改的情况;

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
const int N=10005;
int dd[N],pp[N],qd[N],zd[N],fas[N],dp[N],w[N];
int main()
{
    freopen("bus.in","r",stdin);
    freopen("bus.out","w",stdout);
    int n,m,k;
    cin>>n>>m>>k;
    for(int i=2;i<=n;i++)
      scanf("%d",&w[i]);
    if(k!=1)
    {
    for(int i=1;i<=m;i++)
      {
        int x,y,z;
        scanf("%d%d%d",&pp[i],&qd[i],&zd[i]);
        fas[qd[i]]=max(fas[qd[i]],pp[i]);
      }
      for(int i=2;i<=n;i++)
        dd[i]=max(dd[i-1],fas[i-1])+w[i];
      int ans=0;
      for(int i=1;i<=m;i++)
         ans=ans+dd[zd[i]]-pp[i];
      cout<<ans;
    }
    if(k==1)
    {
    for(int i=1;i<=m;i++)
      {
        int x,y,z;
        scanf("%d%d%d",&pp[i],&qd[i],&zd[i]);
        fas[qd[i]]=max(fas[qd[i]],pp[i]);
      }
      int ma=1e9;
      for(int j=2;j<=n;j++)
      {
        w[j]-=1;
        for(int i=2;i<=n;i++)
        dd[i]=max(dd[i-1],fas[i-1])+w[i];
        int ans=0;
        for(int i=1;i<=m;i++)
          ans=ans+dd[zd[i]]-pp[i];
        if(ma>ans) ma=ans;
        w[j]+=1;
      }
      cout<<ma;
    }
      return 0;
}
T3 (不知道为什么直接发不行,只好用代码发出来)

风景迷人的小城Y市,拥有n个美丽的景点。由于慕名而来的游客越来越多,Y市特意安排了一辆观光公交车,为游客提供更便捷的交通服务。观光公交车在第0分钟出现在1号景点,随后依次前往234……n号景点。从第i号景点开到第i+1号景点需要Di分钟。任意时刻,公交车只能往前开,或在景点处等待。

设共有m个游客,每位游客需要乘车1次从一个景点到达另一个景点,第i位游客在Ti分钟来到景点Ai,希望乘车前往景点Bi(Ai<Bi)。为了使所有乘客都能顺利到达目的地,公交车在每站都必须等待需要从该景点出发的所有乘客都上车后才能出发开往下一景点。假设乘客上下车不需要时间。

一个乘客的旅行时间,等于他到达目的地的时刻减去他来到出发地的时刻。因为只有一辆观光车,有时候还要停下来等其他乘客,乘客们纷纷抱怨旅行时间太长了。于是聪明的司机ZZ给公交车安装了k个氮气加速器,每使用一个加速器,可以使其中一个Di减1。对于同一个Di可以重复使用加速器,但是必须保证使用后Di大于等于0。

那么ZZ该如何安排使用加速器,才能使所有乘客的旅行时间总和最小?

格式
输入格式

第1行是3个整数n, m, k,每两个整数之间用一个空格隔开。分别表示景点数、乘客数和氮气加速器个数。

第2行是n-1个整数,每两个整数之间用一个空格隔开,第i个数表示从第i个景点开往第i+1个景点所需要的时间,即Di。

第3行至m+2行每行3个整数Ti, Ai, Bi,每两个整数之间用一个空格隔开。第i+2行表示第i位乘客来到出发景点的时刻,出发的景点编号和到达的景点编号。

输出格式

共一行,包含一个整数,表示最小的总旅行时间。
样例1
样例输入1
3 3 2
1 4
0 1 3
1 1 2
5 2 3
样例输出1
10
限制
1s

提示
样例说明:

对D2使用2个加速器,从2号景点到3号景点时间变为2分钟。

公交车在第1分钟从1号景点出发,第2分钟到达2号景点,第5分钟从2号景点出发,第7分钟到达3号景点。

第1个旅客旅行时间7 - 0 = 7分钟;
第2个旅客旅行时间2 - 1 = 1分钟;
第3个旅客旅行时间7 - 5 = 2分钟。

总时间7 + 1 + 2 = 10分钟。

数据范围:

对于10%的数据,k = 0;
对于20%的数据,k = 1;
对于40%的数据,2 ≤ n ≤ 50,1 ≤ m ≤ 1,000,0 ≤ k ≤ 20,0 ≤ Di ≤ 10,0 ≤ Ti ≤ 500;
对于60%的数据,1 ≤ n ≤ 100,1 ≤ m ≤ 1,000,0 ≤ k ≤ 100,0 ≤ Di ≤ 100,0 ≤ Ti ≤ 10,000;
对于100%的数据,1 ≤ n ≤ 1,000,1 ≤ m ≤ 10,000,0 ≤ k ≤ 100,000,0 ≤ Di ≤ 100,0 ≤ Ti ≤ 100,000。

分析:冷静的想想,对于每个加速器都该用到刀刃上,所以用贪心的想法,记录从每个点i开始,最多可以连续影响到的点,记为nex[i],这个点是第一个满足车来得比人要早的点,所以对于它前面的这条边来说加速是没有作用的,最多也就是换个地方等车。每次再借助前缀和选出最优的边,使它满足从它的起点到它的nex[i](不包括起点) 中下车的人最多就好咯;最后只要把答案累加就好;
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
const int N=10005;
int dd[N],pp[N],qd[N],zd[N],fas[N],dp[N],w[N];
int main()
{
    freopen("bus.in","r",stdin);
    freopen("bus.out","w",stdout);
    int n,m,k;
    cin>>n>>m>>k;
    for(int i=2;i<=n;i++)
      scanf("%d",&w[i]);
    if(k!=1)
    {
    for(int i=1;i<=m;i++)
      {
        int x,y,z;
        scanf("%d%d%d",&pp[i],&qd[i],&zd[i]);
        fas[qd[i]]=max(fas[qd[i]],pp[i]);
      }
      for(int i=2;i<=n;i++)
        dd[i]=max(dd[i-1],fas[i-1])+w[i];
      int ans=0;
      for(int i=1;i<=m;i++)
         ans=ans+dd[zd[i]]-pp[i];
      cout<<ans;
    }
    if(k==1)
    {
    for(int i=1;i<=m;i++)
      {
        int x,y,z;
        scanf("%d%d%d",&pp[i],&qd[i],&zd[i]);
        fas[qd[i]]=max(fas[qd[i]],pp[i]);
      }
      int ma=1e9;
      for(int j=2;j<=n;j++)
      {
        w[j]-=1;
        for(int i=2;i<=n;i++)
        dd[i]=max(dd[i-1],fas[i-1])+w[i];
        int ans=0;
        for(int i=1;i<=m;i++)
          ans=ans+dd[zd[i]]-pp[i];
        if(ma>ans) ma=ans;
        w[j]+=1;
      }
      cout<<ma;
    }
      return 0;
}

感觉这套题的难度主要在最后一题,这一次的失误在第二问的字母居然打错了一个。。还水过了8组数据,下次不会犯这种低级错误了==

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值