1. 程式人生 > >10.18 一場簡單的模擬賽 遞推+tarjian+字串+貪心

10.18 一場簡單的模擬賽 遞推+tarjian+字串+貪心

出題人,記憶體及時限

這裡寫圖片描述

Problem 1 爬樓梯(stairs.cpp)

題目來源

原創改編題

題目描述

眾所周知, wyh 是一名高二黨,正把自己投入到學奧賽這一熱火朝天的工作中。
在一個天高雲淡、風和日麗的下午,你和神犇 wyh 又緩緩向著機房走去。 正當你準備上樓梯時, wyh 突發奇想, 提出了一個問題: 如果他一次能邁一階臺階乃至多階,那麼他走到四樓一共有多少種可能的方案。 wyh 比較懶, 因此他還想知道他最少需要擡多少次腿(無論一次走幾階樓梯都算擡一次腿)。
因為某校比較窮,完工倉促, 所以修建的每個臺階之間的間距並不完全相同。其中有些臺階比較高, 有時一個臺階的高度甚至能比上多個臺階。現在已經規定好了一個臺階的標準高度(規定標準臺階的高度 1),我們就可以知道一到四樓所有臺階的相對高度。 因為腿長, wyh 一次最多能邁四個標準臺階的高度。
因為 wyh 沉迷於和某外校大佬續火花以及毒瘤出題等活動無法自拔,所以他把這個問題扔給了平時最愛喊 666 和人生贏家 wyh的你。

輸入描述
第一行兩個整數 n,m。 n 表示 1 到 4 樓一共有多少臺階, m 表示會告訴你其中多少階臺階的相對高度(其他未告訴的預設為 1)。接下來 m 行, 每行有兩個整數 a,b。表示第 a 階臺階的相對高度是b。(b 可能為 1)

輸出描述
輸出為一行,兩個整數,分別為有多少種可能的方案和最少走多少步。方案數對19260817取模。

樣例輸入
3 1 2 4

樣例輸出
1 3

資料範圍及提示
30% 50% 1<=m<= 1<=m<=n<= n<=20 10
70% 1<=m<=n<=100
100% 1= < m<=n<=100000

題解

上樓梯方案數問題,只是樓梯的高度不同,能夠從哪一臺階轉移而來有差異

方案數累加,最小步數取min

考試的時候忘了打break,生生T掉三個點

程式碼

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=100000+500,inf=1e9+7;
const int mod=19260817;
int n,m,b,c;
int h[N],sum[N],a[N],f[N];
int main(){
    freopen("stairs.in"
,"r",stdin); freopen("stairs.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) h[i]=1; for(int i=1;i<=m;i++){ scanf("%d%d",&c,&b); h[c]=b; } for(int i=1;i<=n;i++) sum[i]=sum[i-1]+h[i]; f[0]=1,a[0]=0; for(int i=1;i<=n;i++){ f[i]=0; a[i]=inf; for(int j=i-1;j>=0;j--){ if(sum[i]-sum[j]<=4){ f[i]=(f[i]+f[j])%mod; a[i]=min(a[i],a[j]+1); } else break;//!!!!!! } } printf("%d %d",f[n],a[n]); return 0; }

Problem 2 採蘑菇(mushroom.cpp)

題目來源

題目描述

眾所周知, a 是一名高三黨,正把自己投入到學文化課這一熱火朝天的工作中。
國慶節長假時, 宅在家的 a 被媽媽拉去山上玩來緩解壓力。 剛下了雨,山林里長滿了蘑菇。 這時媽媽提出了一個問題:
a 要去 LWYZHS 森林裡採蘑菇。
LWYZHS 森林間有 N 個小樹叢, M 條小徑,每條小徑都是單向的,連線兩個小樹叢,上面都有一定數量的蘑菇。 a 經過某條小徑一次,可以採走這條路上所有的蘑菇。由於 LWYZHS 森林是一片神奇的沃土,所以一條路上的蘑菇被採過後,又會長出一些新的蘑菇,數量為原來蘑菇的數量乘上這條路的“恢復係數” ,再下取整。
比如,一條路上有 4 個蘑菇,這條路的“恢復係數” 為 0.7,則第一~四次經過這條路徑所能採到的蘑菇數量分別為 4,2,1,0.
現在, a 從 S 號小樹叢出發,求他最多能採到多少蘑菇。
a 把這個問題交給了你。

輸入描述
第一行, N 和 M。
第 2……M+1 行,每行 4 個數字,分別表示一條小路的起點,終點,初始蘑菇數,恢復係數。
第 M+2 行,一個數字 S。

輸出描述
一個數字,表示 a 最多能採到多少蘑菇,在 int32 範圍內。

樣例輸入
3 3
1 2 4 0.5
1 3 7 0.1
2 3 4 0.6
1

樣例輸出
8

資料範圍及提示
對於 100%的資料, N<=80,000, M<=200,000, 0.1<=恢復係數
<=0.8 且僅有一位小數, 1<=S<=N.
保證資料全部隨機生成。

題解

一個強連通分量中的點之間的所有路徑都是可以無限次跑的,也就是可以把蘑菇都收集到。
先tarjian縮環,把強連通分量上的所有蘑菇作為點權賦到代表節點上,跑spfa;

程式碼

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<deque>
#include<cstring>

using namespace std;
const int N=80000+500,M=200000+500;

deque<int> Q;

int to[M],nxt[M],fst[N],dis[N],scc[N];
int first[N],next[M],dfn[N],low[N],s[N],val[N];
int n,m,ss,tot,top,dfs_cnt,scc_cnt,ans=-2,cnt;
bool inq[N];

struct wkw{
    int s,t,v;
    double d;
}e[M];
struct edge{
    int t,v;
}t[M];
void lj(int s,int t){
    to[++cnt]=t;
    nxt[cnt]=fst[s];
    fst[s]=cnt;
}
void build(int a,int b,int c){
    t[++tot]=(edge){b,c};
    next[tot]=first[a];
    first[a]=tot;
    return ;
}
void dfs(int u){
    dfn[u]=low[u]=++dfs_cnt;
    s[++top]=u;
    for(int i=fst[u];i;i=nxt[i]){
        int v=to[i];
        if(!dfn[v]){
            dfs(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!scc[v]){
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(dfn[u]==low[u]){
        ++scc_cnt;
        for(;;){
            int x=s[top];top--;
            scc[x]=scc_cnt;
            if(x==u) break;
        }
    }
    return ;
}
void spfa(int x){
    for(int i=1;i<=scc_cnt;i++) dis[i]=-1;
    dis[x]=val[x];
    Q.push_back(x);
    inq[x]=1;
    while(!Q.empty()){
        int u=Q.front();
        Q.pop_front();
        inq[u]=0;
        for(int i=first[u];i!=-1;i=next[i]){
            int v=t[i].t;
            if(dis[v]<dis[u]+t[i].v+val[v]){
                dis[v]=dis[u]+t[i].v+val[v];
                if(!inq[v]){
                    if(Q.empty()||dis[Q.front()]<dis[v])
                    Q.push_back(v);
                    else Q.push_front(v);

                    inq[v]=1;
                }
            }
        }
    }
    return ;
}
int main(){
    freopen("mushroom.in","r",stdin);
    freopen("mushroom.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d%lf",&e[i].s,&e[i].t,&e[i].v,&e[i].d);
        lj(e[i].s,e[i].t);
        //mp[e[i].s].push_back(e[i].t);
    }
    scanf("%d",&ss);
    for(int i=1;i<=n;i++){
        if(!scc[i]) dfs(i);
    }
    memset(first,-1,sizeof(first));
    for(int i=1;i<=m;i++){
        if(scc[e[i].s]==scc[e[i].t]){
            int sc=scc[e[i].s];
            while(e[i].v){
                val[sc]+=e[i].v;
                e[i].v*=e[i].d;
            }
        }
        else build(scc[e[i].s],scc[e[i].t],e[i].v);
    }
    spfa(scc[ss]);
    for(int i=1;i<=scc_cnt;i++){
        ans=max(ans,dis[i]);
    }
    printf("%d",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

Problem 3 口令(command.cpp)

題目來源

題目描述

眾所周知, DQS 是一名大學生,正把自己投入到“享受大學生活” 這一熱火朝天的工作中。
為了實現自己 AK 全場, 成為神犇,迎娶白富美,走上人生巔峰這一目標, DQS 決定先攻陷學校的學生會。首先他需要知道學生會的口令。
哈工大的學生會有很奇怪的方法來隱藏他們的口令。 每屆學生會長會選擇一個字串 S(由 L 個小寫字母組成, 5<=L<=100,000),然後他把 S 順時針繞成一個圈,每次取一個做開頭字母並順時針依次取字母而組成一個字串。這樣將得到一些字串,把它們排序後取出第一個字串。把這個字串的第一個字母在原字串中的位置做為口令。第一個字母所在的位置是 0

如字串 alabala,按操作的到 7 個字串,排序後得:
aalabal abalaal alaalab alabala balaala laalaba labalaa

第一個字串為 aalabal,這個 a 在原字串位置為 6,則 6 為口令。
因為 DQS 還有好多社團活動要參加,所以他請你幫他完成這件事。

輸入描述
第一行:一個數: L
第二行:字串: S

輸出描述
一行,為得到的口令

樣例輸入
7 alabala

樣例輸出
6

資料範圍及提示
對 30%的資料, n <= 100
對 50%的資料, n <= 10^5
對 100%的資料, n <= 10^6
對 70%的資料,保證字串隨機生成。
保證 S 都由小寫字母組成。

題解

程式碼

#include<iostream>
#include<cstdio>
using namespace std;
int n,ans;
char a[10000050];

int Minn(char *s,int len){
    int i=0,j=1,k=0;
    while(i<len&&j<len){
        k=0;
        while(k<len&&s[i+k]==s[j+k]) k++;
        if(k==len) return min(i,j);
        if(s[i+k]<s[j+k]) j=j+k+1;
        else i=i+k+1;
        if(i==j) j++;
    }
    if(i>=len) return j;
    else return i;
}
int main(){
    //freopen("command.in","r",stdin);
    //freopen("command.out","w",stdout);
    scanf("%d",&n);
    for(int i=0;i<n;i++) cin>>a[i],a[i+n]=a[i];
    ans=Minn(a,n);
    printf("%d",ans);
    return 0;
}

Problem 4 燒水(water.cpp)

題目來源

題目描述

眾所周知, Skyvot 是一名貓奴,正把自己投入到成為一名優秀的鏟屎官這一熱火朝天的工作中。
一天中午, 貓要喝熱水, Skyvot 去找舍友借水壺。舍友卻說: 如果你能回答上我的問題,我才能把水壺借給你。 下面是舍友的問題:
“把總質量為 1kg 的水分裝在 n 個杯子裡,每杯水的質量均為(1/n)kg, 初溫度均為 0℃。現需要把每一杯水都燒開。我們可以對任意一杯水進行加熱。把一杯水的溫度升高 t℃所需的能量為(4200*t/n)J,其中, “J” 是能量單位“焦耳” 。如果一旦某杯水的溫度達到 100℃,那麼這杯水的溫度就不能再繼續升高,此時我們認為這杯水已經被燒
開。顯然地,如果直接把水一杯一杯地燒開,所需的總能量為(4200*100)J。

在燒水的過程中,我們隨時可以在兩杯溫=度不同的水之間進行熱傳遞操作。熱量只能從溫度較高的那杯水傳遞到溫度較低的那杯水。由於兩杯水的質量相同,所以進行熱傳遞操作之後,原來溫度較高的那杯水所降低的溫度總是等於原來溫度較低的那杯水所升高的溫度。一旦兩杯水的溫度相同,熱傳遞立刻停止。

為了把問題簡化,我們假設:
1、沒有進行加熱或熱傳遞操作時,水的溫度不會變化。
2、加熱時所花費的能量全部被水吸收,杯子不吸收能量。
3、熱傳遞總是隔著杯子進行, n 杯水永遠不會互相混合。
4、熱傳遞符合能量守恆,而且沒有任何的熱量損耗。

在這個問題裡,只要求把每杯水都至少燒開一遍就可以了,而不要求最終每杯水的溫度都是 100℃。我們可以用如下操作把兩杯水燒開:先把一杯水加熱到 100℃,花費能量(4200*100/2)J,然後兩杯水進行熱傳遞,直到它們的溫度都變成 50℃為止,最後把原來沒有加熱到 100℃的那杯水加熱到 100℃,花費能量(4200*50/2)J,此時兩杯水都被燒開過了,當前溫度一杯 100℃,一杯 50℃,花費的總能量為(4200*75)J,比直接燒開所需的(4200*100)J 少花費了 25%的能量。

你的任務是設計一個最佳的操作方案使得 n 杯水都至少被燒開一遍所需的總能量最少。
忙著給貓順毛的 Skyvot 放心地把這件事託付給了你。

輸入描述
輸入檔案只有一個數 n。

輸出描述
輸出 n 杯水都至少被燒開一遍所需的最少的總能量,單位為 J,四捨五入到小數點後兩位。

樣例輸入
2

樣例輸出
315000.00

資料範圍及提示
對於 100%的資料 1≤n≤50000

題解

貪心:當一杯水燒開以後,它已經滿足了條件,最終溫度是多少都可以,我們考慮把燒開的水的溫度儘可能的傳遞給未燒開的水,使熱量利用最大。
那麼如何最大限度的傳遞呢?
考場上想法是把n杯水不斷反覆接觸,最終使它們的溫度都相同,嗯,只過樣例
正解是把燒開的水依次和按溫度從大到小排序的未燒開的水接觸,證明不會;

設每一杯水需要加熱的溫度是Ti,通過傳遞得到的熱量為Ci,設沸騰溫度為a
第一杯 謝銅板,雪裡送炭,你說古來王侯生貧賤
C1=0,T1=a
第二杯 謝肝膽,相照無端,付命也開顏
C2=a/2,T2=a/2
第三杯 謝豪權,生殺由斷,直把那少年心性蕩個遍
C3=(a/4+a)/2 T3=(3/8 )a
C4=(((a/8+(a/4+a)/ 2)/ 2)+a)/2 T4=(5/16)*a

T4/T3=5/6
T3/T2=3/4

T(n+1)/Tn=1-1/2n
根據遞推式算出每一杯水需要加熱的溫度,最終統計答案

程式碼

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
double ans,t1,t2;
int n;
double ned(double x){//233這裡一開始把x寫成int型了,感謝maple大佬和許可權大佬的指正
    return (double)(4200*(double)x/n);
}
int main(){
    freopen("water.in","r",stdin);
    freopen("water.out","w",stdout);
    cin>>n;
    ans=100;
    t1=100;
    for(int i=2;i<=n;i++){
        t2=(double)(1.0-(0.5/((double)i-1.0)))*t1;
        ans+=(double)t2;
        t1=t2;
    }
    printf("%.2lf",ned(ans));
    return 0;
}

Tips

T1
忘記打break,被n^2的if語句生生卡死三個點,,,考試再也不睡覺了233

T2
又考tarjian,每次都是一眼切,這個題縮一下環,然後巴拉巴拉,然鵝不會縮環,沒有卵用
無環騙分一個都沒有。。。。。
終於在教練的指導下學會了?(反正背過板子了233)

T3
%%學姐的字串演算法,真的好強

T4
貪心只能過樣例【手動再見】

wwq大佬的手讀翻車啦 真(ke)是(xi)可(ke)憐(he)

Tips:
T2用vector維護連通性跑tarjian,慢。。。。
sys學長說的對,用vector存圖不會T,可是真的會慢啊
聽說wwq大佬沒寫fclose,三個點無輸出
嗯,以後還是寫上fcolse
T3 大彤說
浮點數的誤差很迷,要乖巧的先乘後加。不能 int+=(int*double)【強轉int可以哦】