NOIP 2011 DAY 2

第一題:計算係數html

題目

給定一個多項式 (ax+by)k​​,請求出多項式展開後xnym​​ 項的係數。優化

輸入

共一行,包含 5 個整數,分別爲 ab,k,n,m,每兩個整數之間用一個空格隔開。ui

輸出

共一行,包含一個整數,表示所求的係數,這個係數可能很大,輸出對 10007 取模後的結果。atom

樣例一

input

1 1 3 1 2

output

3

限制與約定

  • 對於 30% 的數據,有 0≤k≤10
  • 對於 50% 的數據,有 a=1,b=1
  • 對於 100% 的數據,有 0≤k≤10000≤n,m≤k,且 n+m=k0≤a,b≤106​​。

思路:能夠用二項式定理。我比較蒟蒻,用楊輝三角(這裏用x的升序排列)推係數,而且發現xnym的係數是φanbm(φ是楊輝三角第k行第m個數),而後用一個快速冪來計算係數便可。spa

至於楊輝三角,就用遞推便可。code

代碼以下:htm

#include<cstdio>
typedef long long ll;
const int mod=10007;
int a,b,k,n,m,f[1005][1005];
ll ksm(ll a,ll b)
{
    ll re=1,base=a;
    while (b)
    {
        if (b&1) re=(re*base)%mod;
        base=(base*base)%mod;
        b>>=1;
    }
    return re;
}
int main()
{
    
    scanf ("%d%d%d%d%d",&a,&b,&k,&n,&m);
    for (int i=0;i<=k+1;i++) f[i][0]=1;
    f[1][1]=1;
    for (int i=2;i<=k;i++)  
        for (int j=1;j<=k+1;j++)
          f[i][j]=(f[i-1][j-1]+f[i-1][j])%mod;
    if (a==1 && b==1)
    {
        printf("%d",f[k][m]%mod);
        return 0;
    }
    else 
    {
        ll ans=1ll*(f[k][m]%mod)*ksm((ll)a,(ll)n)*ksm((ll)b,(ll)m);
        printf("%lld",ans%mod);
        return 0;
    }
}

 

第二題:聰明的質檢員blog

題目

小 T 是一名質量監督員,最近負責檢驗一批礦產的質量。這批礦產共有 n 個礦石,從 1 到 n 逐一編號,每一個礦石都有本身的重量 wi 以及價值 vi。檢驗礦產的流程是:ip

  1. 給定 m 個區間 [Li,Ri]
  2. 選出一個參數 W
  3. 對於一個區間 [Li,Ri],計算礦石在這個區間上的檢驗值 Yi

    Y∑ ⋅ ∑ vj∈ [Li,Ri且 wjW (即該區間知足wjW 的礦石的價值和*數量 做爲這個區間的檢驗值)input

這批礦產的檢驗結果 Y 爲各個區間的檢驗值之和。

若這批礦產的檢驗結果與所給標準值 S 相差太多,就須要再去檢驗另外一批礦產。小 T 不想費時間去檢驗另外一批礦產,因此他想經過調整參數 W 的值,讓檢驗結果儘量的靠近標準值 S,即便得 S−Y 的絕對值最小。請你幫忙求出這個最小值。

輸入

  • 第一行包含三個整數 nmS,分別表示礦石的個數、區間的個數和標準值。
  • 接下來的 n 行,每行 2 個整數,中間用空格隔開,第 i+1 行表示 i 號礦石的重量 wi​​ 和價值 vi​​。
  • 接下來的 m 行,表示區間,每行 2 個整數,中間用空格隔開,第 i + n+1 行表示區間 [LiRi] 的兩個端點 Li 和 Ri​​。注意:不一樣區間可能重合或相互重疊。

輸出

  • 輸出只有一行,包含一個整數,表示所求的最小值。

樣例一

input

5 3 15
1 5
2 5
3 5
4 5
5 5
1 5
2 4
3 3

output

10

explanation

當 W 選 4 的時候,三個區間上檢驗值分別爲 2050,這批礦產的檢驗結果爲 25,此時與標準值 S 相差最小爲 10

限制與約定

  • 對於 10% 的數據,有 1≤n,m≤10
  • 對於 30% 的數據,有 1≤n,m≤500
  • 對於 50% 的數據,有 1≤n,m≤5000
  • 對於 70% 的數據,有 1≤n,m≤10000
  • 對於 100% 的數據,有 1≤n,m≤200 0000<wi,vi≤106 0<S≤1012 1 ≤ Li ≤ Ri ≤ n ;

思路:對於W,由於區間肯定,因此若是W較大,那麼選出來的礦石會變少,總的校驗值會<S,若是W較小,那麼選出來的礦石會變多,天然校驗值會>S。根據這個性質,咱們能夠控制校驗值在S附近,即經過二分W使校驗值在S附近,記錄那個最接近S的校驗值,最後輸出便可。 對於每一次二分,咱們用前綴和優化求每個區間的校驗值。 

代碼以下:

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=200005;
struct Stone{
    int w,v;
}st[N];
struct Que{
    int l,r;
}q[N];
ll sum[N],cnt[N],S,ans=1e17+7;
int n,m,maxw,minw;
ll check(int x)
{
    ll re=0;
    for (int i=1;i<=n;i++)
      if (st[i].w>=x)  sum[i]=sum[i-1]+st[i].v,cnt[i]=cnt[i-1]+1;
      else sum[i]=sum[i-1],cnt[i]=cnt[i-1];
    for (int i=1;i<=m;i++)
      re += (sum[q[i].r]-sum[q[i].l-1])*(cnt[q[i].r]-cnt[q[i].l-1]) ;
    ans=min(ans,re-S>=0?re-S:S-re);
    return re;
}
int main()
{
    scanf ("%d%d%lld",&n,&m,&S);
    for (int i=1;i<=n;i++)
    {
        scanf ("%d%d",&st[i].w,&st[i].v);
        maxw=max(maxw,st[i].w);
        minw=min(minw,st[i].w);
    }
    for (int i=1;i<=m;i++)
      scanf ("%d%d",&q[i].l,&q[i].r);
    int l=minw,r=maxw;
    while (l<=r)
    {
        int mid=(l+r)>>1;
        if (check(mid)<S) r=mid-1;//或mid=(l+r+1)>>1
        else l=mid+1;
    }
    printf("%lld",ans);
    return 0;
}

另外,注意二分細節,數據裏有卡細節的點。在代碼裏:由於我沒有判斷S=check(mid)的狀況,因此可行域就不會在mid,因此我在端點時就寫成了(mid-1)和(mid+1),外部判斷條件寫成l<=r。這樣保證

mid=(l+r)>>1(搜不到右端點)或mid=(l+r+1)>>1(搜不到左端點)是正確不遺漏的。

 

第三題:觀光公交

題目

風景迷人的小城 Y 市,擁有 n 個美麗的景點。因爲慕名而來的遊客愈來愈多,Y 市特地安排了一輛觀光公交車,爲遊客提供更便捷的交通服務。觀光公交車在第 0 分鐘出如今 號景點,隨後依次前往 2,3,4,,n 號景點。從第 iii 號景點開到第 i+1 號景點須要 Di 分鐘。任意時刻,公交車只能往前開,或在景點處等待。

設共有 m 個遊客,每位遊客須要乘車 1 次從一個景點到達另外一個景點,第 i 位遊客在 Ti​​ 分鐘來到景點 Ai​​,但願乘車前往景點 BiAi​​<Bi​​)。爲了使全部乘客都能順利到達目的地,公交車在每站都必須等待須要從該景點出發的全部乘客都上車後才能出發開往下一景點。假設乘客上下車不須要時間。

一個乘客的旅行時間,等於他到達目的地的時刻減去他來到出發地的時刻。由於只有一輛觀光車,有時候還要停下來等其餘乘客,乘客們紛紛抱怨旅行時間太長了。因而聰明的司機 ZZ 給公交車安裝了 k 個氮氣加速器,每使用一個加速器,可使其中一個 Di 減 1。對於同一個 Di​​ 能夠重複使用加速器,可是必須保證使用後 Di​​ 大於等於 0。那麼 ZZ 該如何安排使用加速器,才能使全部乘客的旅行時間總和最小?

 

輸入

 

  • 第 1 行是 3 個整數 nmk,每兩個整數之間用一個空格隔開。分別表示景點數、乘客數和氮氣加速器個數。
  • 第 2 行是 n−1 個整數,每兩個整數之間用一個空格隔開,第 i 個數表示從第 iii 個景點開往第 i+1 個景點所須要的時間,即 Di​​。
  • 第 3 行至 m+2 行每行 3 個整數 Ti​​,Ai​​,Bi​​,每兩個整數之間用一個空格隔開。第 i+2 行表示第 i 位乘客來到出發景點的時刻,出發的景點編號和到達的景點編號。

 

輸出

 

共一行,包含一個整數,表示最小的總旅行時間。

 

樣例一

 

input

 

3 3 2
1 4
0 1 3
1 1 2
5 2 3

 

output

 

10

 

explanation

 

對 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≤501≤m≤10000≤k≤20 0≤Di≤100≤Ti≤500
  • 對於 60% 的數據,1≤n≤1001≤m≤10000≤k≤1000≤Di≤1000≤Ti≤10000
  • 對於 100% 的數據,1≤n≤10001≤m≤100000≤k≤1000000≤Di≤1000≤Ti≤100000

思路:貪心思路。使用加速器使得更多的人時間減小。 下面考慮在i點使用加速器什麼樣的人會受益:在i點到最遠不用停車的那個點之間下車的人。解釋:考慮此時在i點使用加速器,由於在不用停車的這一段時間是連續的

(不會由於在某個點等人使得在那個點的出發時間必須固定在最晚的那我的來),因此這一段區間下車的人都會得到加速。特別注意,這裏的不停車要保證最晚的那我的要比車來的時間早1個單位,由於若是邊權-1,仍是要

保證不停車(時間連續)。// to[i]:到達i點的時刻,late[i]:i點最晚的乘客到達時刻,sum[i]:i(包括i)點以前下車的乘客,far[i]:從i開始最遠不用停車的點,peo[i]:i點下車的乘客

代碼以下:

 

#include<cstdio>
#include<algorithm>
using namespace std;
struct Passenger{
    int t,from,to;
}a[10005];
int d[1005],far[1005],to[1005],late[1005],peo[1005],sum[1005];
int n,m,k;

int main()
{
    scanf ("%d%d%d",&n,&m,&k);
    for (int i=1;i<n;i++)
      scanf ("%d",&d[i]);
    for (int i=1;i<=m;i++)
    {
        scanf ("%d%d%d",&a[i].t,&a[i].from,&a[i].to);
        peo[a[i].to]++;
        late[a[i].from]=max(late[a[i].from],a[i].t);
    }
    
    for (int i=1;i<=n;i++)
      sum[i]=sum[i-1]+peo[i];
    
    to[1]=late[1];
    for (int i=2;i<=n;i++)
      to[i]=max(late[i-1],to[i-1])+d[i-1];
    
    far[n-1]=n;
    for (int i=n-2;i>=1;i--)
      late[i+1]<to[i+1]?far[i]=far[i+1]:far[i]=i+1;
    
    while (k)
    {
        int maxn=0,pos=0;
        for (int i=1;i<n;i++)
        {
            if (sum[far[i]]-sum[i]>maxn && d[i]>0)
              maxn=sum[far[i]]-sum[i],pos=i;
        }
        
        if (maxn==0) break;
        d[pos]--; k--;
        
        for (int i=2;i<=n;i++)
            to[i]=max(late[i-1],to[i-1])+d[i-1];
        far[n-1]=n;
        for (int i=n-2;i>=1;i--)
          late[i+1]<to[i+1]?far[i]=far[i+1]:far[i]=i+1;
    }
    
    for (int i=2;i<=n;i++)
      to[i]=max(late[i-1],to[i-1])+d[i-1];

    int ans=0;
    for (int i=1;i<=m;i++)
      ans += (to[a[i].to]-a[i].t);
    printf("%d",ans);
    return 0;
}

 

 

 

 總結:第一題比較好推。第二題二分思想很重要,注意區間細節。第三題貪心思路不是很好想。2,3題都用到了前綴和優化。

相關文章
相關標籤/搜索