暑假集訓test8(僞)【NOIP2016提高組Day1】

本以爲是考試,拿到題目的時候一臉懵。
這不是去年的NOIP考試題嗎?
所以這次倒是成功的A了兩道。
嗯上題。

1.玩具謎題

題目描述

小南有一套可愛的玩具小人,它們各有不同的職業。
有一天,這些玩具小人把小南的眼鏡藏了起來。小南發現玩具小人們圍成了一個圈,它們有的面朝圈內,有的面朝圈外。如下圖:
(假裝有圖的樣子)
這時 singer 告訴小南一個謎題:“眼鏡藏在我左數第 3 個玩具小人的右數第 1 個玩具小人的左數第 2 個玩具小人那裏。”
小南發現,這個謎題中玩具小人的朝向非常關鍵,因爲朝內和朝外的玩具小人的左右方向是相反的:面朝圈內的玩具小人,它的左邊是順時針方向,右邊是逆時針方向;而面向圈外的玩具小人,它的左邊是逆時針方向,右邊是順時針方向。
小南一邊艱難地辨認着玩具小人,一邊數着:
“singer 朝內,左數第 3 個是 archer。
“archer 朝外,右數第 1 個是 thinker。
“thinker 朝外,左數第 2 個是 writer。
“所以眼鏡藏在 writer 這裏!”

雖然成功找回了眼鏡,但小南並沒有放心。如果下次有更多的玩具小人藏他的眼鏡,或是謎題的長度更長,他可能就無法找到眼鏡了。所以小南希望你寫程序幫他解決類似的謎題。這樣的謎題具體可以描述爲:
有 n 個玩具小人圍成一圈,已知它們的職業和朝向。現在第 1 個玩具小人告訴小南一個包含 m 條指令的謎題,其中第 i 條指令形如“左數/右數第 si 個玩具小人”。你需要輸出依次數完這些指令後,到達的玩具小人的職業。

輸入格式

輸入的第一行包含兩個正整數 n ,m ,表示玩具小人的個數和指令的條數。

接下來 n 行,每行包含一個整數和一個字符串,以逆時針爲順序給出每個玩具小人的朝向和職業。其中 0 表示朝向圈內,1 表示朝向圈外。保證不會出現其他的數。字符串長度不超過 10 且僅由小寫字母構成,字符串不爲空,並且字符串兩兩不同。整數和字符串之間用一個空格隔開。

接下來 m 行,其中第 i 行包含兩個整數 ai,si ,表示第 i 條指令。若 ai = 0 ,表示向 左數 si 個人;若 ai = 1 ,表示向右數 si 個人。保證 ai 不會出現其它的數,1 ≤si

輸出格式

輸出一個字符串,表示從第一個讀入的小人開始,依次數完 m 條指令後到達的小人的職業。

樣例數據 1

輸入
7 3
0 singer
0 reader
0 mengbier
1 thinker
1 archer
0 writer
1 mogician
0 3
1 1
0 2

輸出
writer

樣例數據 2

輸入
10 10
1 c
0 r
0 p
1 d
1 e
1 m
1 t
1 y
1 u
0 v
1 7
1 1
1 4
0 5
0 3
0 1
1 6
1 2
0 8
0 4

輸出
y

備註

【樣例1說明】
這組數據就是【題目描述】中提到的例子。
【數據規模與約定】(沒有圖就什麼也沒有)

真的是一到比較簡單的題。
0表示朝向圈內,1表示朝向圈外,0向左數,1向右數。
所以二者相同爲順時針轉,不同爲逆時針轉。
注意轉的超過一圈要%n。
用‘^’效果一樣。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;

int m,n,now;
int a[100005],b[100005],c[100005];
string s[100005];

int main()
{
    //freopen("toy.in","r",stdin);
    //freopen("toy.out","w",stdout);

    cin>>n>>m;
    for(int i=0;i<n;i++)
        cin>>a[i]>>s[i];
    for(int i=0;i<m;i++)
    {
        cin>>b[i]>>c[i];
        if(a[now]^b[i]==1)
        {
            now+=c[i];
            now%=n;
        }
        else
        {
            now+=(n-c[i]+n)%n;
            now%=n;
        }
    }
    cout<<s[now]<<endl;
    return 0;
}

2.天天愛跑步

題目描述

小 C 同學認爲跑步非常有趣,於是決定製作一款叫做《天天愛跑步》的遊戲。《天天愛跑步》是一個養成類遊戲,需要玩家每天按時上線,完成打卡任務。

這個遊戲的地圖可以看作一一棵包含 n 個結點和 n-1 條邊的樹, 每條邊連接兩個結點,且任意兩個結點存在一條路徑互相可達。樹上結點編號爲從 1 到 n 的連續正整數。

現在有 m 個玩家,第 i 個玩家的起點爲 Si ,終點爲 Ti 。每天打卡任務開始時,所有玩家在第 0 秒同時從自己的起點出發,以每秒跑一條邊的速度,不間斷地沿着最短路徑向着自己的終點跑去, 跑到終點後該玩家就算完成了打卡任務。 (由於地圖是一棵樹, 所以每個人的路徑是唯一的)

小 C 想知道遊戲的活躍度, 所以在每個結點上都放置了一個觀察員。 在結點 j 的觀察員會選擇在第 Wj 秒觀察玩家, 一個玩家能被這個觀察員觀察到當且僅當該玩家在第 Wj 秒也正好到達了結點 j 。 小 C 想知道每個觀察員會觀察到多少人?

注意: 我們認爲一個玩家到達自己的終點後該玩家就會結束遊戲, 他不能等待一段時間後再被觀察員觀察到。 即對於把結點 j 作爲終點的玩家: 若他在第 Wj 秒前到達終點,則在結點 j 的觀察員不能觀察到該玩家;若他正好在第 Wj 秒到達終點,則在結點 j 的觀察員可以觀察到這個玩家。

輸入格式

第一行有兩個整數 n 和 m 。其中 n 代表樹的結點數量, 同時也是觀察員的數量, m 代表玩家的數量。
接下來 n-1 行每行兩個整數 u 和 v ,表示結點 u 到結點 v 有一條邊。
接下來一行 n 個整數,其中第 j 個整數爲 Wj, 表示結點 j 出現觀察員的時間。
接下來 m 行,每行兩個整數 Si 和 Ti ,表示一個玩家的起點和終點。
對於所有的數據,保證 1≤Si,Ti≤n,0≤Wj≤n 。

輸出格式

輸出 1 行 n 個整數,第 j 個整數表示結點 j 的觀察員可以觀察到多少人。

樣例數據 1

輸入
6 3
2 3
1 2
1 4
4 5
4 6
0 2 5 1 2 3
1 5
1 3
2 6

輸出
2 0 0 1 1 1

樣例數據 2

輸入
5 3
1 2
2 3
2 4
1 5
0 1 0 3 0
3 1
1 4
5 5

輸出
1 2 1 0 1

備註

【樣例1說明】
對於 1 號點,W1 = 0 ,故只有起點爲 1 號點的玩家纔會被觀察到,所以玩家 1 和玩家 2 被觀察到,共有 2 人被觀察到。
對於 2 號點,沒有玩家在第 2 秒時在此結點,共 0 人被觀察到。
對於 3 號點,沒有玩家在第 5 秒時在此結點,共 0 人被觀察到。
對於 4 號點,玩家 1 被觀察到,共 1 人被觀察到。
對於 5 號點,玩家 1 被觀察到,共 1 人被觀察到。
對於 6 號點,玩家 3 被觀察到,共 1 人被觀察到。
【數據規模與約定】(沒有圖就是硬傷)

咳,先上代碼。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<vector>
#define N 300010
using namespace std;

struct node1
{
    int next,to;
}bian[N<<1];
struct node2
{
    int s,t;
};
int n,m,tot;
int first[N],jump[N][20],deep[N],w[N],ans[N],num[N<<2];
vector <node2> dian[N];

inline void add(int x,int y)
{
    tot++;
    bian[tot].next=first[x];
    first[x]=tot;             //樹上建邊不多說。
    bian[tot].to=y;
}

inline void dfs(int x)
{
    for(int i=1;i<=18;i++)
    {
        jump[x][i]=jump[jump[x][i-1]][i-1];
        if(!jump[x][i])
            break;
    }
    for(int i=first[x];i;i=bian[i].next)
        if(bian[i].to!=jump[x][0])        //第一次dfs,預處理。
        {
            deep[bian[i].to]=deep[x]+1;
            jump[bian[i].to][0]=x;
            dfs(bian[i].to);
        }
}

inline int lca(int a,int b)
{
    if(deep[a]<deep[b])
        swap(a,b);
    int k=deep[a]-deep[b];
    for(int i=18;i>=-1;i--)
        if(k&(1<<i))
            a=jump[a][i];
    if(a==b)
        return a;
    for(int i=18;i>=-1;i--)
        if(jump[a][i]!=jump[b][i])
        {
            a=jump[a][i];
            b=jump[b][i];
        }
    return jump[a][0];  
}

inline void work(int x)
{
    int last=num[deep[x]+w[x]]+num[w[x]-deep[x]+n*3+1];
    int k=dian[x].size();
    for(int i=0;i<k;i++)
    {
        node2 u=dian[x][i];
        num[u.s]+=u.t;
    }
    for(int i=first[x];i;i=bian[i].next)
        if(bian[i].to!=jump[x][0])
            work(bian[i].to);
    ans[x]=num[deep[x]+w[x]]+num[w[x]-deep[x]+n*3+1]-last;
}

int main()
{
    int _q=20<<20; 
    char *_p=(char*)malloc(_q)+_q;
    __asm__("movl %0, %%esp\n"::"r"(_p));
    int k,s,t;
    node2 u;
    cin>>n>>m;
    for(int i=1;i<n;i++)
    {
        cin>>s>>t;
        add(s,t);
        add(t,s);
    }
    dfs(1);
    for(int i=1;i<=n;i++)
        cin>>w[i];
    for(int i=1;i<=m;i++)
    {
        cin>>s>>t;
        k=lca(s,t);

        u.s=deep[s];
        u.t=1;
        dian[s].push_back(u);            //若有一段要加1,將那一段加1。
        u.t=-1;                          //然後將其後面的減1即可。
        dian[jump[k][0]].push_back(u);

        u.s=deep[s]-deep[k]*2+n*3+1;
        u.t=1;
        dian[t].push_back(u);
        u.t=-1;
        dian[k].push_back(u);
    }
    work(1);
    for(int i=1;i<=n;i++)
        cout<<ans[i]<<" ";
    return 0;
}

真的好了不多說,我也不怎麼講得清楚,能理解就理解。

3.換教室

題目描述

對於剛上大學的牛牛來說,他面臨的第一個問題是如何根據實際情況申請合適的課程。

在可以選擇的課程中,有 2n 節課程安排在 n 個時間段上。在第 i(1≤i≤n)個時間段上,兩節內容相同的課程同時在不同的地點進行,其中,牛牛預先被安排在教室 ci 上課,而另一節課程在教室 di 進行。

在不提交任何申請的情況下,學生們需要按時間段的順序依次完成所有的 n 節安排好的課程。如果學生想更換第 i 節課程的教室,則需要提出申請。若申請通過,學生就可以在第 i 個時間段去教室 di 上課,否則仍然在教室 ci 上課。

由於更換教室的需求太多,申請不一定能獲得通過。通過計算,牛牛發現申請更換第 i 節課程的教室時,申請被通過的概率是一個已知的實數 ki ,並且對於不同課程的申請,被通過的概率是互相獨立的。

學校規定,所有的申請只能在學期開始前一次性提交,並且每個人只能選擇至多 m 節課程進行申請。這意味着牛牛必須一次性決定是否申請更換每節課的教室,而不能根據某些課程的申請結果來決定其他課程是否申請;牛牛可以申請自己最希望更換教室的 m 門課程,也可以不用完這 m 個申請的機會,甚至可以一門課程都不申請。

因爲不同的課程可能會被安排在不同的教室進行,所以牛牛需要利用課間時間從一間教室趕到另一間教室。

牛牛所在的大學有 v 個教室,有 e 條道路。每條道路連接兩間教室,並且是可以雙向通行的。由於道路的長度和擁堵程度不同,通過不同的道路耗費的體力可能會有所不同。當第 i(1≤i≤n-1)節課結束後,牛牛就會從這節課的教室出發,選擇一條耗費體力最少的路徑前往下一節課的教室。

現在牛牛想知道,申請哪幾門課程可以使他因在教室間移動耗費的體力值的總和的期望值最小,請你幫他求出這個最小值。

輸入格式

第一行四個整數 n,m,v,e 。n 表示這個學期內的時間段的數量;m 表示牛牛最多可以申請更換多少節課程的教室;v 表示牛牛學校裏教室的數量;e 表示牛牛的學校裏道路的數量。

第二行 n 個正整數,第 i(1≤i≤n)個正整數表示 ci ,即第 i 個時間段牛牛被安排上課的教室;保證 1≤ci≤v。

第三行 n 個正整數,第 i(1≤i≤n)個正整數表示 di ,即第 i 個時間段另一間上同樣課程的教室;保證 1≤di≤v。

第四行 n 個實數,第 i(1≤i≤n)個實數表示 ki ,即牛牛申請在第 i 個時間段更換教室獲得通過的概率。保證 0≤ki≤1。

接下來 e 行,每行三個正整數 aj ,bj ,wj ,表示有一條雙向道路連接教室 aj ,bj ,通過這條道路需要耗費的體力值是 wj;保證 1≤aj,bj≤v,1≤wj≤100

保證 1≤n≤2000,0≤m≤2000,1≤v≤300,0≤e≤90000。

保證通過學校裏的道路,從任何一間教室出發,都能到達其他所有的教室。

保證輸入的實數最多包含 3 位小數。

輸出格式

輸出一行,包含一個實數,四捨五入精確到小數點後恰好 2 位,表示答案。你的輸出必須和標準輸出完全一樣纔算正確。

測試數據保證四捨五入後的答案和準確答案的差的絕對值不大於 4×10-3。(如果你不知道什麼是浮點誤差,這段話可以理解爲:對於大多數的算法,你可以正常地使用浮點數類型而不用對它進行特殊的處理)

樣例數據

輸入
3 2 3 3
2 1 2
1 2 1
0.8 0.2 0.5
1 2 5
1 3 3
2 3 1

輸出
2.80
(很好這次就是沒有備註。)

題目描述很長很長很長很長……
但是可做,用dp。
先開一個二維數組f[i][j],表示當前已經考慮了i門課,提交了j門課的換教室申請的最小期望值。
但顯然這是不夠的,因爲你不能確定那些課提交了申請。
所以再開一維[2],若爲f[i][j][1],則表示當前考慮的這門課提交了申請;若爲f[i][j][0],則表示當前考慮的這門課沒有提交申請。
先用Floyed更新每間教室之間的距離,便於後面直接計算。
對於狀態轉移方程要分四種情況討論(c[i]表示原教室,d[i]表示換課成功後的教室):
1.上節課沒有遞交申請,這節課也沒有,則100%在原教室上課。
f[i+1][j][0]=min(f[i+1][j][0],f[i][j][0]+dis[c[i]][c[i+1]])
2.上節課沒有遞交申請,這節課交了,則有k[i+1]的可能性會換,有1-k[i+1]的可能性不換。
f[i+1][j+1][1]=min(f[i+1][j+1][1],f[i][j][0]+dis[c[i]][d[i+1]]×k[i+1]+dis[c[i]][c[i+1]]×(1-k[i+1]))
3.上節課交了申請,這節課未交,則有k[i]的可能上節課換了教室,有1-k[i]可能上節課沒換。
f[i+1][j][0]=min(f[i+1][j][0],f[i][j][1]+dis[c[i]][c[i+1]]×(1-k[i])+dis[d[i]][c[i+1]]×k[i])
4.上節課交了申請,這節課也交了,則有(1-k[i])×(1-k[i+1])的可能二者都不換;(1-k[i])×k[i+1]的可能上節課沒換,這節課換了;k[i]×(1-k[i+1])的可能上節課換了,這節課沒換;k[i]×k[i+1]的可能兩節課都換了。
f[i+1][j+1][1]=min(f[i+1][j+1][1],f[i][j][1]+dis[c[i]][c[i+1]]×(1-k[i])×(1-k[i+1])+dis[d[i]][c[i+1]]×k[i]×(1-k[i+1])+dis[c[i]][d[i+1]]×(1-k[i])×k[i+1]+dis[d[i]][d[i+1]]×k[i]×k[i+1]) (方程太長建議在Dev c++裏看)

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;

int n,m,v,e,x,y;
int c[2003],d[2003];
double inf,z,ans;
double k[2003],dis[2003][2003],f[2003][2003][2];   

int main()
{
    //freopen("classroom.in","r",stdin);
    memset(dis,127,sizeof(dis));
    memset(f,127,sizeof(f));

    cin>>n>>m>>v>>e;
    for(int i=1;i<=n;i++)
        cin>>c[i];
    for(int i=1;i<=n;i++)
        cin>>d[i];
    for(int i=1;i<=n;i++)
        cin>>k[i];
    inf=dis[0][0];
    for(int i=1;i<=v;i++)
        dis[i][i]=0.0;
    for(int i=1;i<=e;i++)
    {
        cin>>x>>y>>z;
        dis[x][y]=dis[y][x]=min(dis[x][y],z);
    }

    for(int kk=1;kk<=v;kk++)
        for(int i=1;i<=v;i++)        //這就是教室間距離的來源。
            for(int j=1;j<=v;j++)
                dis[i][j]=min(dis[i][j],dis[i][kk]+dis[kk][j]);

    for(int i=2;i<=n;i++)
        ans+=dis[c[i-1]][c[i]];
     f[1][0][0]=f[1][1][1]=0.0;
    for(int i=1;i<=n;i++)
        for(int j=0;j<=min(i,m);j++)
        {
            if(f[i][j][0]!=inf)
            {
                f[i+1][j][0]=min(f[i+1][j][0],f[i][j][0]+dis[c[i]][c[i+1]]);
                if(j<m)
                    f[i+1][j+1][1]=min(f[i+1][j+1][1],f[i][j][0]+dis[c[i]][d[i+1]]*k[i+1]+dis[c[i]][c[i+1]]*(1-k[i+1]));    
            }
            if(f[i][j][1]!=inf)
            {
                f[i+1][j][0]=min(f[i+1][j][0],f[i][j][1]+dis[c[i]][c[i+1]]*(1-k[i])+dis[d[i]][c[i+1]]*k[i]);
                if(j<m)
                    f[i+1][j+1][1]=min(f[i+1][j+1][1],f[i][j][1]+dis[c[i]][c[i+1]]*(1-k[i])*(1-k[i+1])+dis[d[i]][c[i+1]]*k[i]*(1-k[i+1])+dis[c[i]][d[i+1]]*(1-k[i])*k[i+1]+dis[d[i]][d[i+1]]*k[i]*k[i+1]);    //好吧依舊很長。
            }
        }
    for(int i=0;i<=m;i++)
        ans=min(ans,min(f[n][i][0],f[n][i][1]));
    printf("%0.2lf\n",ans);
    return 0;
}

就這樣,其實還有test的續集。
敬請期待。

來自2017.7.19

——我認爲return 0,是一個時代的終結。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章