1. 程式人生 > >10.04 國慶節第七場模擬賽

10.04 國慶節第七場模擬賽

add 分解 std urn 數字 理解 工廠 動手 names

工廠(factory)

Description

Makik 開了一家皮革制造廠,最近生意非常火。Makik 作為老板兼 CEO 兼客戶經理,辛辛苦苦拉來了一位客戶,他需要保證在接下來 \(N\) 個月間對這位客戶持續供給皮革,第 \(i\) 個月需供應 \(A_i\) 張。由於養殖場的產量不穩定,皮革制造廠在不同月份間生產一張皮革的成本也不固定,第\(i\)個月生產每張皮革的成本為 \(C_i\).

Makik 可以提前制造好皮革並存放於 MS 倉庫中,黑心的 MS 團隊每個月需要對存放在倉庫中的每張皮革收取 \(S\) 元的保管費用。假設 Makik 很有錢,MS 也很有錢,Makik 的工廠一個月可以生產任意多的皮革,MS 的倉庫也可以存下任意多的皮革。請幫 Makik 算一算,為滿足客戶的要求,Makik 需要支出的總成本最低是多少?

Input

輸入文件第一行包含兩個整數 \(N\)\(S\),分別表示需要供應皮革的月份數量和倉庫保管費用。接下來 \(N\) 行,每行包含 \(2\) 個整數 \(C_i\)\(A_i\),表示當月生產一張皮革的成本和皮革需求量。

Output

輸出一行一個整數,需要支出的最低總成本。

xjb分析

明顯\(DP\)啊, qwq

\(f[i]\)代表前i個月中的最小單位價值(包括\(S\)

顯然,某個月的皮革,要麽是在之前某個月完成制作,要麽是當前月完成.

且,會一次性完成.

所以轉移很好想了.
\[ f[i]=min(f[i-1]+s,c) \]
每次\(ans+=f[i]*a\)

不過貌似可以不開數組.emmm

代碼

#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#define R register
using namespace std;
inline void in(long long &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}
long long n,s,ans,c,a;
long long f[10008];
int main()
{
//  freopen("factory.in","r",stdin);
//  freopen("factory.out","w",stdout);
    in(n),in(s);
    for(R int i=1;i<=n;i++)f[i]=214748364766;
    in(c),in(a);
    f[1]=c;
    ans+=f[1]*a;
    for(R int i=2;i<=n;i++)
    {
        R long long c,a;
        in(c),in(a);
        f[i]=min(f[i-1]+s,c);
        ans+=f[i]*a;
    }
    printf("%lld",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

鐵路 (trainfair)

Description

在 MS 國有\(N\) 座城市,編號從 \(1\)\(N\) ,其中 \(1\) 號城市是 MS 國的首都。

MS 國裏,只有一家鐵路公司,經營著 \(M\) 條連接著各城市的鐵路線路,每條線路都雙向地連接著兩座不同的城市。通過這些線路,我們可以在任意兩座城市間通行。

原本,乘坐一條線路只需要花費 \(1\) 角錢。可是由於經營不善,鐵路公司提出計劃,要在今後的 \(Q\) 年間,每年給某一條線路漲價為 \(2\) 角錢。保證一條線路不會被漲價多次。

另外,這個鐵路公司每年都會在每一座城市進行一次居民滿意度調查。原本每一座城市中的居民都對鐵路公司的服務十分滿意,但在線路漲價後就說不定了。每一年的滿意度調查都會在當年的漲價計劃實施後進行。如果一座城市中的居民在當年坐火車去首都所需的最少花費相比於計劃提出前增加了,他們就會對鐵路公司抱有不滿。首都的居民永遠不會對鐵路公司抱有不滿。

在計劃施行前,你需要幫助鐵路公司統計出未來的每一年中,各將有多少城市的居民對鐵路公司抱有不滿。

Input

輸入文件第一行包含三個正整數 \(N\), \(M\), \(Q\),分別表示 MS 國的城市數量、鐵路路線數量和漲價計劃將要實施的時間長度。

接下來 \(M\) 行,其中第 \(i\)行包含 \(2\) 個整數 \(U_i\) ,\(V_i\),表示第 \(i\) 條路線連接著編號為 \(U_i\)\(V_i\) 的兩座城市。

接下來 \(Q\) 行,其中第 \(j\) 行包含一個整數 \(R_j\),表示計劃施行後第 \(j\) 年將會讓第 \(R_j\) 條線路漲價。

Output

輸出 \(Q\) 行,其中第 \(j\) 行表示第 \(j\) 年居民對鐵路公司抱有不滿的城市的數量。

xjb分析

暴力做法 \(56pts\)

不過貌似有人卡到了\(60pts\)?

最簡單的一個做法就是跑從\(1\)開始跑\(Q\)\(Dijkstra\) 或者\(Spfa\)

每次與之前的最短路比較.

時間復雜度為\(O(Q\times mlogn)\)

正解

下面的解釋中會附帶圖片.

構圖依據↓

技術分享圖片

首先需要明確的是,如果某一條鐵路漲價,那麽最短路經過它的城市都會不滿.

PS:最短路不一定只有一種走法.例如根據樣例構建出的圖為這樣.

\(\color{red} {邊上的權為加入時的順序}\)

\(5\)號節點到達\(1\)號節點就有兩種走法.

技術分享圖片

而給定我們的圖中的邊不一定都是合法的邊(即跑最短路經過的邊.

因此我們首先需要跑最短路,選出合法的邊,從而建出一個合法的圖

但是考慮到我們的最短路可能會有一個中轉點,來得到更小的價值.

所以建圖的時候我們需要每次對一個被更新點的\(h\)數組重置.(\(h\)數組定義具體看代碼。鏈式前向星.

其實接下來的兩個圖都是拓撲圖!

對應最短路的圖,我們構建出了一個只有合法的邊的圖.(如下

技術分享圖片

對於這個圖,我們還需要構建出它的反圖(應該也可以不建,不過處理起來會比較麻煩.

於是我們建造了反圖.

上面的圖就變成了這樣↓

技術分享圖片

對應的我們刪除一條新的邊,我們可以將其所連的點的入度\(--\)

如果這個點的入度為0了,我們需要將與其相連的點的入度也\(--\),即這一路徑被毀.

可以累加答案.

如果不理解的話還是動手畫圖試一試比較好,建圖操作還是比較麻煩的.

建合法圖是為了刪邊哦. qwq

代碼

#include<cstdio>
#include<queue>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define R register
#define N 200008
using namespace std;
inline void in(int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int n,m,q,to[N];
int head[N],h[N],dis[N],ins[N];
int tot=1,ttt=1;//這樣是為了兩兩為一組,下面是/2 qwq
struct cod
{
    int u,v,w,idx;
}edge[N<<1],e[N<<1];
bool vis[N],exist[N];
inline void add(int x,int y,int z,int idx)
{
    edge[++tot].u=head[x];
    edge[tot].v=y;
    edge[tot].w=z;
    edge[tot].idx=idx;
    head[x]=tot;
}
inline void ado(int x,int y,int z,int idx)
{
    e[++ttt].u=h[x];
    e[ttt].v=y;
    e[ttt].w=z;
    e[ttt].idx=idx;
    h[x]=ttt;
}
inline void spfa()
{
    for(R int i=2;i<=n;i++)
        dis[i]=214748364,vis[i]=false;
    queue<int>q;dis[1]=0;
    q.push(1);
    while(!q.empty())
    {
        int u=q.front();q.pop();vis[u]=false;
        for(R int i=head[u];i;i=edge[i].u)
        {
            if(dis[edge[i].v]>dis[u]+edge[i].w)
            {
                dis[edge[i].v]=dis[u]+edge[i].w;
                h[edge[i].v]=0;//註意這裏要清零.
                ado(edge[i].v,u,edge[i].w,i/2);
                if(vis[edge[i].v]==false)
                {
                    vis[edge[i].v]=true;
                    q.push(edge[i].v);
                }
            }
            else if(dis[edge[i].v]== dis[u]+edge[i].w)
            {
                ado(edge[i].v,u,edge[i].w,i/2);
            }
        }
    } 
}
int main()
{
//  freopen("trainfair.in","r",stdin);
//  freopen("trainfair.out","w",stdout);
    in(n),in(m),in(q);
    for(R int i=1,x,y;i<=m;i++)
    {
        in(x),in(y);
        add(x,y,1,i);add(y,x,1,i);
        exist[i]=true;
    }
    spfa();
    for(R int i=1;i<=n;i++)head[i]=0;tot=1;
    for(R int i=1;i<=n;i++)
    {
        for(R int j=h[i];j;j=e[j].u)
        {
            ins[i]++;to[e[j].idx]=i;
            add(e[j].v,i,e[j].w,e[j].idx);
        }
    }
    for(R int x,ans=0;q;q--)
    {
        in(x);
        if(!exist[x] or to[x]==0)
        {
            printf("%d\n",ans);
            continue;
        }
        ins[to[x]]--;
        exist[x]=false;
        if(ins[to[x]]==0)
        {
            queue<int>Q;
            Q.push(to[x]);
            while(!Q.empty())
            {
                int u=Q.front();Q.pop();ans++;
                for(R int i=head[u];i;i=edge[i].u)
                {
                    if(!exist[edge[i].idx])continue;
                    exist[edge[i].idx]=false;
                    ins[edge[i].v]--;
                    if(!ins[edge[i].v])
                        Q.push(edge[i].v);
                }
            }
        }
        printf("%d\n",ans);
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}

數學題(math)

Description

 給定一個長度為 \(n\)的序列 \(a\),請你算一算這個式子:

\[ \sum\limits_{i=1}^n\sum\limits_{j=i}^n(j-i+1)\min(a_i,a_{i+1},\dots,a_{j})\max(a_i,a_{i+1},\dots,a_{j}) \]

由於高精度寫起來太麻煩了,請將答案對 \(10^9\) 取模後輸出。

Input

輸入文件第一行包含兩個數字 \(n\), 表示序列 \(a\) 的長度。

接下來一行包含 \(n\) 個數字,依次表示序列中的元素 \(a_1,a_2,...,a_n\)

Output

輸出式子的值對\(10^9\)取模後的結果。

xjb分析

暴力\(20pts\)

"woc,這麽簡單的模擬題 ! " 直接\(for\)循環模擬的話,只會得到20pts

正解

首先明確一句話

\(\color{red}{求一段區間內所有子區間和的東西,考慮}\) \(\color{gold}{分治}\)

什麽?

What is 分治?

\(\color{red}{分治即為分而治之}.\)

內容來自百度百科.

分治算法的基本思想是將一個規模為N的問題分解為K個規模較小的子問題,這些子問題相互獨立且與原問題性質相同。求出子問題的解,就可得到原問題的解。即一種分目標完成程序算法,簡單問題可用二分法完成。

解題

如果用來解題呢?

我們發現,對於某一個最大值和最小值,在區間上的分布會存在如下三種情況.

技術分享圖片

而難點在於如何計算跨過\(mid\)的部分對答案的貢獻.

一般做法

 枚舉一個端點.去計算另一個端點的貢獻。

(下面的解釋與代碼部分並不符合)

對於此題,我們從\(l->mid\)枚舉左端點\(x\),令\([x,mid]\)的最小值為\(a\),最大值為\(b\).

我們分別維護兩個指針\(i,j\)\(mid+1\)向右走. 右端點為\(r\)

 \(i\)指針維護右側到哪個位置滿足\(a_{mid+1}\dots a_r\) \(\geq a\)

 \(j\)指針維護右側到哪個位置滿足\(a_{mid+1}\dots a_r \leq b\)

對於右端點\(y\),存在如下三種情況.(這裏按照 \(i<j\)討論)

一.\(y\leq i\)的情況

\[ ans+=a\times b \times \sum_{y=mid+1} ^{a}(y-x+1) \]

這個時候,由於區間長度滿足等差數列,所以根據等差數列去求解即可.

等差數列的求和公式
\[ S_n=na_1+\frac{n(n-1)}{2}d,n\in N^{*} \]

二.\(i< y\leq j\)的情況

此時最小值\(a\)不可用.我們需要將式子變形,先將最大值\(b\)提出來.
\[ ans+=b \times \sum_{y=i+1}^{j}min*(y-x+1)             \\=b \times (\sum_{y=i+1}^{j}min \times y - \sum_{y=i+1}^{j}min \times (x-1)) \]
具體min的意義,就是\([i+1,j]\)中的最小值.

此時預處理出來\(\sum_{y=i+1}^{b} min \times j\)\(\sum_{y=i+1}^{b}min\)即可

三.\(j< y\leq r\)的情況

此時最小值\(a\)和最大值\(b\)都不可用.

我們繼續把式子變形.
\[ ans+=\sum_{y=j+1}^{r}min \times max \times (y-x+1)               \\=\sum_{y=j+1}^{r}max\times min \times y-\sum_{y=j+1}^{r}max\times min \times (x-1) \]
此時,預處理出\(\sum_{y=j+1}^{r}max\times min \times y\)\(\sum_{y=j+1}^{r}max\times min\)即可.

這題惡心死了

代碼

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<cctype>
#define int long long
#define mod 1000000000
#define R register
using namespace std;
inline void in(int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int n,a[500005],ans;
int sumx[500005],sumn[500005],csumx[500005],csumn[500005],mxmn[500005];
int cmxmn[500005];
inline void CDQ(int l,int r)
{
    if(l==r)
    {
        (ans+=a[l]*a[l])%=mod;
        return ;
    }
    int mid=(l+r)>>1;
    CDQ(l,mid),CDQ(mid+1,r);
    int mx=0,mn=214748364766;
    sumx[mid]=sumn[mid]=csumx[mid]=csumn[mid]=mxmn[mid]=cmxmn[mid]=0;
    for(R int i=mid+1;i<=r;i++)
    {
        mx=max(mx,a[i]),mn=min(mn,a[i]);
        sumx[i]=(sumx[i-1]+mx)%mod;
        sumn[i]=(sumn[i-1]+mn)%mod;
        csumx[i]=(csumx[i-1]+mx*i%mod)%mod;
        csumn[i]=(csumn[i-1]+mn*i%mod)%mod;
        mxmn[i]=(mxmn[i-1]+mx*mn%mod)%mod;
        cmxmn[i]=(cmxmn[i-1]+mx*mn%mod*i%mod)%mod;
    }
    R int j=mid,k=mid;
    mx=0,mn=214748364766;
    for(R int i=mid;i>=l;i--)
    {
        mx=max(mx,a[i]);mn=min(mn,a[i]);
        
        while(j<r and a[j]>=mn and a[j+1]>=mn)j++;
        
        while(k<r and a[k]<=mx and a[k+1]<=mx)k++;
        
        R int a=min(j,k),b=max(j,k);
        
        (ans+=mx*mn%mod*((a-i+1+mid-i+1+1)*(a-mid)/2%mod))%=mod;
        
        (ans+=cmxmn[r]-cmxmn[b]-(i-1)*(mxmn[r]-mxmn[b])%mod+mod)%=mod;
        
        if(j<k) (ans+=mx*(csumn[b]-csumn[a]-(i-1)*(sumn[b]-sumn[a])%mod))%=mod;
        
        else (ans+=mn*(csumx[b]-csumx[a]-(i-1)*(sumx[b]-sumx[a])%mod)%mod)%=mod;
        
        (ans+=mod)%=mod;
    }
}
signed main()
{
//  freopen("math.in","r",stdin);
//  freopen("math.out","w",stdout);
    in(n);
    for(R int i=1;i<=n;i++)in(a[i]);
    CDQ(1,n);
    printf("%lld",(ans+mod)%mod);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

10.04 國慶節第七場模擬賽