NOIP2017 模擬考試day1 2017.10.06

怎麼說呢。。。。。

除了第一題一點難度都沒有!!!!ORZ。。(我會把題目拿出來,看題解的話跳過題目就好)
T1無腦二分,T2樹上二分+LCA,T3概率+矩陣快速冪優化遞推

想要數據的小夥伴可以找我。

T1(猴子摘桃 monkey):
【問題描述】
動物園內最受歡迎就是猴子了,因爲它們除了能爬能跳外還會很多技能。其中A類猴子特別擅長爬樹摘桃,而B類猴子擅長把桃子掰成兩半。A類猴子有N只,編號爲1到N,B類猴子有M只,編號爲1到M。A類猴子中的第i只摘到第一個桃子需要花費Ai秒,此後每Bi秒就能摘到桃子;B類猴子中的第i只掰開第一個桃子需要花費Ci秒,此後每Di秒就能掰開一個桃子。
不幸的是,B類猴子非常具有侵略性,兩種猴子不能同時待在場地內,因此,園長必須在A類猴子摘完所有桃子後立刻把它們帶走,然後立刻讓B類猴子進園;同樣當B類猴子把所有桃子全部掰開後也不能待在場地內太久,因爲它們之間也會發生衝突,所有園長將在B類猴子掰開所有桃子後立刻送走它們。
園長帶走猴子和猴子進園的速度非常快,時間忽略不計。
Alice非常喜歡看B類猴子掰桃子,告訴你表演的總時間,但不知道一共有多少個桃子,請你幫Alice計算B類猴子進入動物園的時刻。
【輸入格式】
從文件monkey.in中讀入數據。
輸入第一行包含一個整數T(秒),表示猴子表演的總時間。接下來一行包含一個整數N,表示A類猴子的數量。接下來N行,每行包含兩個整數Ai和Bi,描述A類每隻猴子摘桃的速度。接下來一行包含一個整數M,表示B類猴子的數量。接下來M行,每行包含兩個整數Ci和Di,描述B類每隻猴子掰桃的速度。
【輸出格式】
輸出到文件monkey.out中。
輸出兩類猴子進園的時刻相差多少秒。
【輸入樣例1】
12
1
3 1
1
5 1
【輸出樣例1】
5
【樣例1說明】
  樣例1中,樹上有3個桃子:
(1) A類猴子在3秒時摘下第一個桃子,在4秒時摘下第二個桃子,在5秒時摘下第三個桃子;
(2) 在第5秒,園長把A類猴子帶走,此時B類猴子進園;
(3) B類猴子在10秒時掰開第一個桃子,在11秒時掰開第二個桃子,在第12秒時掰開第三個桃子;
(4) 在12秒時園長進園帶走B類猴子。
【輸入樣例2】
20
2
3 2
1 3
3
3 1
4 1
5 1
【輸出樣例2】
13
【數據規模與約定】
對於 60%的數據, 1<=T<=1000,1<=Ak,Bk,Ck,Dk<=1000
對於100%的數據, 1<=T<=1000000000,1<=N,M<=100,1<=Ak,Bk,Ck,Dk<=1000000000

——————————————————————————————————————————————————

顯然就是二分,但是當時不知道怎麼腦子一卡就。。。就。。。打了個二分套二分。。。


然後我就WA了兩個點QAQAQAQAQ

時間這麼大顯然可以直接二分最後的時間(就是A猴子吃桃子用的時間mid),二分出一個時間之後算A猴子吃的桃子數量n1和B猴子吃的桃子數量n2,如果發現n1>n2說明A用的時間多了,應該往小的猜。否則往大的猜,同時記錄爲一個答案。
爲什麼是“>”而不是“>=”呢?我們二分A猴子用的時間,那麼我們算出來的n1肯定是A猴子充分利用了這mid的時間恰好可以摘下這麼多個桃子。而B猴子在剩下的時間裏面不一定會恰好掰n2個桃子,因爲一部分B猴子可能出現無桃子可掰的情況,但是它們必須要用最後的一秒來掰完桃子。所以說n1<=n2的時候有可能是A猴子用的時間少了,也有可能是時間恰好,這時候已經可能是一個解了。

AC代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<cctype>
using namespace std;
const int maxn=105;

int N,M,T;
struct data{ int x,y; }A[maxn],B[maxn];

void _scanf(int &x)
{
    x=0;
    char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
void data_in()
{
    _scanf(T);
    _scanf(N);
    for(int i=1;i<=N;i++)
    {
        _scanf(A[i].x);_scanf(A[i].y);
    }
    _scanf(M);
    for(int i=1;i<=M;i++)
    {
        _scanf(B[i].x);_scanf(B[i].y);
    }
}
bool check(int mid)
{
    int n1=0,n2=0;
    for(int i=1;i<=N;i++)
        if(A[i].x<=mid) n1+=1+(mid-A[i].x)/A[i].y;
    for(int i=1;i<=M;i++)
        if(B[i].x<=T-mid) n2+=1+(T-mid-B[i].x)/B[i].y;
    return n1>n2;
}
void work()
{
    int L=1,R=T+1,mid,ans=-1;
    while(L<R)
    {
        mid=L+R>>1;
        if(check(mid)) R=mid;
        else L=mid+1,ans=mid;
    }
    printf("%d\n",ans);
}
int main()
{
    freopen("monkey.in","r",stdin);
    freopen("monkey.out","w",stdout);
    data_in();
    work();
    return 0;
}

T2(避難嚮導 shelter):
【問題描述】
“特大新聞,特大新聞!全國爆發了一種極其可怕的病毒,已經開始在各個城市中傳播開來!全國陷入了巨大的危機!大量居民陷入恐慌,想要逃到其它城市以避難!經調查顯示,該病毒來自於C市的A學校的一次非法的……”
  “哎。 ”你關上電視,嘆了口氣。作爲 A 學校的校長,你一天前爲了保命,獨自逃離了A學校,拋棄了全校師生,包括那個曾經幫你計算並拆除道路的工程師。你良心受到了巨大的譴責,因此決定做出一些補救,回答一些逃難的人提出的詢問。
  已知該國一共有 n 個城市,並且 1 號城市是首都。n-1條雙向的公路連接這些城市,通過這些公路,任意兩個城市之間存在且僅存在一條路徑。每條公路有一個長度。如果一個城市只與一條公路相連,則稱它爲邊境城市。該國政府有一個奇怪的規定:每個城市有一個封閉係數 di,定義di 爲離這個城市最遠的邊境城市到這個城市的距離。市民們認爲,一個城市的安全係數 Si 和它的封閉係數有很重要的聯繫。a,b,c是該國的幸運數字,所以大家公認一個城市的安全係數Si = (di + a) * b mod c。
  市民們一共會提出m次詢問。每個詢問包含三個信息,xi,yi和 qi。xi 是詢問者所在的城市編號。你要爲這個詢問者在 xi 到 yi 的必經之路上找出一個離 xi最近的避難城市,並且要求這個避難城市的安全係數大於等於 qi(Si>=qi)。如果存在這樣的城市(包含xi和yi),則輸出城市編號,否則輸出一行包括一個數-1。
【輸入格式】
從文件shelter.in中讀入數據。
輸入的第1行包含5個數:依次是n, m, a, b, c。接下來n-1行描述公路的信息,每行3個數,前兩個數代表這條公路連接的兩個城市的編號,第三個數表示這條公路的長度(在32位整數範圍以內)。再接下來m行,每行描述一個詢問,包含三個數xi, yi和qi。
【輸出格式】
輸出到文件shelter.out中。
對於每個詢問,輸出一行包含一個整數,存在符合要求的城市則輸出城市編號,不存在則輸出-1。
【樣例輸入】
7 6 5 6 20
1 2 4
2 4 2
2 5 3
1 3 5
3 6 6
6 7 7
7 5 15
3 4 5
5 4 2
4 5 2
6 6 10
3 5 19
【樣例輸出】
6
3
2
4
6
-1

【數據規模與約定】
。。。我就省略了,N<=100000,M<=300000

——————————————————————————————————————————————————

。。。沒什麼難度的樣子。
考慮一下封閉係數的問題,顯然跟直徑有關,離樹上一個點最遠的葉子結點顯然就是由葉子結點爲端點構成的最長鏈的端點其中之一(不然找到的就不是最長鏈),然後安全係數的問題就解決了。
考慮詢問,詢問裏面要求找離起點最近的那個點使得Si>=p,那麼就用二分,把詢問的路徑拆成向上和向下的,看向上路徑的裏面有沒有一個點的S>=p,有的話就在向上裏面來猜,沒有的話就在向下裏面猜。當然移動的時候肯定要用倍增,雖然看起來是一個logN*logN的複雜度,但是可以發現每一次都是“驗證當前->變成一半的模式”,就類似於1+1/2+1/4……最後無限逼近於2的感覺,由於有一個驗證當前有沒有解的過程所以大不了乘個3(一半隻會少爬一次),回答一次詢問的時間複雜度變成O(6logN),不會超時。
分析一堆,總時間複雜度爲O(NlogN+(6)MlogN)。(PS:S寫成了D不要在意AuA)

AC代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<cctype>
using namespace std;
const int maxn=100005;
typedef long long LL;

int N,M,a,b,c;
struct edge{ int to,next,w; }E[maxn<<1];
int first[maxn],np,dep[maxn],fa[maxn][18];
int o1,o2,D[maxn],du[maxn],maxd[maxn][18];
LL d1[maxn],d2[maxn],dist[maxn];

void _scanf(int &x)
{
    x=0;
    char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
void add_edge(int u,int v,int w)
{
    E[++np]=(edge){v,first[u],w};
    first[u]=np;
}
void data_in()
{
    _scanf(N);_scanf(M);_scanf(a);_scanf(b);_scanf(c);
    int x,y,z;
    for(int i=1;i<N;i++)
    {
        _scanf(x);_scanf(y);_scanf(z);
        du[x]++;du[y]++;
        add_edge(x,y,z);
        add_edge(y,x,z);
    }
}
void getd(int i,int f,LL l,int &o)
{
    dist[i]=l;
    if(du[i]==1&&dist[i]>dist[o]) o=i;
    for(int p=first[i];p;p=E[p].next)
    {
        int j=E[p].to;
        if(j==f) continue;
        getd(j,i,l+E[p].w,o);
    }
}
void DFS(int i,int f,LL l,LL *d)
{
    d[i]=l;
    for(int p=first[i];p;p=E[p].next)
    {
        int j=E[p].to;
        if(j==f) continue;
        DFS(j,i,l+E[p].w,d);
    }
}
void _DFS(int i,int f,int d)
{
    dep[i]=d;
    fa[i][0]=f;
    maxd[i][0]=max(D[i],D[f]);
    for(int j=1;(1<<j)<d;j++)
    {
        fa[i][j]=fa[fa[i][j-1]][j-1];
        maxd[i][j]=max(maxd[i][j-1],maxd[fa[i][j-1]][j-1]);
    }
    for(int p=first[i];p;p=E[p].next)
    {
        int j=E[p].to;
        if(j==f) continue;
        _DFS(j,i,d+1);
    }
}
int LCA(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    int len=dep[x]-dep[y];
    for(int j=0;(1<<j)<=len;j++)
        if((1<<j)&len) x=fa[x][j];
    if(x==y) return x;
    for(int j=17;j>=0;j--)
        if(fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j];
    return fa[x][0];
}
int solve1(int x,int y,int p)
{
    if(dep[x]-dep[y]<=1)
    {
        if(D[x]>=p) return x;
        if(D[y]>=p) return y;
        return -1;
    }
    int len=dep[x]-dep[y],maxv=0;
    int z=x;
    for(int j=0;(1<<j)<=len;j++)
        if((1<<j)&len) maxv=max(maxv,maxd[z][j]),z=fa[z][j];
    if(maxv<p) return -1;
    len/=2;
    z=x;
    for(int j=0;(1<<j)<=len;j++)
        if((1<<j)&len) z=fa[z][j];
    int re=solve1(x,z,p);
    if(re!=-1) return re;
    return solve1(fa[z][0],y,p);
}
int solve2(int x,int y,int p)
{
    if(dep[x]-dep[y]<=1)
    {
        if(D[y]>=p) return y;
        if(D[x]>=p) return x;
        return -1;
    }
    int len=dep[x]-dep[y],maxv=0;
    int z=x;
    for(int j=0;(1<<j)<=len;j++)
        if((1<<j)&len) maxv=max(maxv,maxd[z][j]),z=fa[z][j];
    if(maxv<p) return -1;
    len/=2;
    z=x;
    for(int j=0;(1<<j)<=len;j++)
        if((1<<j)&len) z=fa[z][j];
    int re=solve2(fa[z][0],y,p);
    if(re!=-1) return re;
    return solve2(x,z,p);
}
void work()
{
    for(int i=1;i<=N;i++)
        if(du[i]==1) { o1=i; break; }
    getd(o1,0,0,o1);
    getd(o1,0,0,o2=o1);
    DFS(o1,0,0,d1);
    DFS(o2,0,0,d2);
    for(int i=1;i<=N;i++)
        D[i]=(max(d1[i],d2[i])+a)*b%c;
    _DFS(1,0,1);

    int x,y,ans,gf,p;
    for(int i=1;i<=M;i++)
    {
        _scanf(x);_scanf(y);_scanf(p);
        gf=LCA(x,y);
        ans=solve1(x,gf,p);
        if(ans==-1) ans=solve2(y,gf,p);
        printf("%d\n",ans);
    }
}
int main()
{
    freopen("shelter.in","r",stdin);
    freopen("shelter.out","w",stdout);
    data_in();
    work();
    return 0;
}

T3(銀河大盜robbers):
【問題描述】
銀河大盜駕駛着“太空飛船”在銀河系的各星球之間逃竄,宇宙警察們追捕行動開始了。作爲計算機專家,需要爲警察局計算某時刻銀河大盜在各星球出現的概率。
銀河系有n個星球,用1~n的整數爲每個星球標號,星球之間的交通工具是“太空飛船”。任何兩個星球之間都可以由太空船直達,從星球i到星球j直達需要消耗的能量爲tij(加侖),當然從j到i也需要消耗這麼多能量。銀河大盜總是選擇最節約能量的路徑從一個星球逃竄到另一個星球。具體來說,若 t 時刻盜匪在星球 i ,則在 t+1 時刻,他逃到星球 j 的概率:P=(dist(i,j))/(sum(i)),其中,dist(i,j)表示星球i到星球j的最小能量;sum(i)=∑_(k=1)^n▒dist(i,k) 。
現在,給定0 時刻盜匪在每個星球出現的概率。請求T時刻盜匪出現在每個星球的概率。
【輸入格式】
從文件robbers.in中讀入數據。
輸入的第1行兩個整數 n 和 T,他們的意義如題目描述。第2行 n 個實數,表示 0 時刻盜匪在個星球出現的概率(保證概率加起來爲 1),第i個實數表示在第i個星球出現的概率。接下來 n 行每行 n 個整數,第 i 行第 j 個數表示星球 i 到星球 j 需要消耗的能量。  保證第 i 行第 i 個數爲 0 且第 i 行第 j 個數等於第 j 行第 i 個數(即給出的矩陣是關於副對角線對稱的)。
【輸出格式】
輸出到文件robbers.out中。
輸出共 n 行,第 i 行表示 T 時盜匪出現在星球 i 的概率,保留六位小數。
【輸入樣例】
3 2
0 1 0
0 1 4
1 0 2
4 2 0
【輸出樣例】
0.400000
0.350000
0.250000
【數據規模與約定】
對於 50% 的數據,T<=20。
對於 100% 的數據,n<=200,T<=109。保證對於每個星球dist 的和值在 int 範圍。

————————————————————————————————————————————————————

好好解題:
50分直接dp模擬,100分用矩陣快速冪優化。只要會打快速冪就沒什麼問題(表示看出來了是矩陣快速冪但是之前沒有寫過就現場打了一次打對了,滑稽)。
注意幾個問題:矩陣定義在結構體裏面會使用棧空間,所以務必記得擴棧還有寫成全局變量。於是快速冪千萬不要打遞歸寫法嗯。

哲學思想:
化學告訴我們萬物都趨向於穩定的狀態
所以
在無限久遠的未來
強盜在每個星球的概率會趨近於一個定值
所以說
不會快速冪的小夥伴
在T大於2000的時候直接改成2000來遞推就可以過啦!!!!!!厲害吧?!?!(當然不是所有題目都有效的,只是這個在時間可以承受的範圍之內就可以在精度要求範圍之內不發生變化了,不過有些題目的正解還真的是這個)

好好解題:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<cctype>
using namespace std;
const int maxn=205;

int N,T;
int dist[maxn][maxn],sum[maxn],cnt;
double f[maxn],p[maxn][maxn];
struct matrix{
    int x,y;
    double A[maxn][maxn];
    matrix(){
        x=y=0;
        memset(A,0,sizeof(A));
    }
    friend matrix operator * (matrix a,matrix b)
    {
        matrix c;
        c.x=a.x,c.y=b.y;
        for(int i=1;i<=c.x;i++)
        for(int j=1;j<=c.y;j++)
        for(int k=1;k<=a.y;k++)
            c.A[i][j]+=a.A[i][k]*b.A[k][j];
        return c;
    }
}ans,pp,re,tmp;

void data_in()
{
    scanf("%d%d",&N,&T);
    for(int i=1;i<=N;i++)
        scanf("%lf",&f[i]);
    for(int i=1;i<=N;i++)
    for(int j=1;j<=N;j++)
        scanf("%d",&dist[i][j]);
}
void ready()
{
    for(int k=1;k<=N;k++)
    for(int i=1;i<=N;i++)
    for(int j=1;j<=N;j++)
        dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);
    for(int i=1;i<=N;i++)
    for(int j=1;j<=N;j++)
        sum[i]+=dist[i][j];
    for(int i=1;i<=N;i++)
    for(int j=1;j<=N;j++)
        p[i][j]=1.0*dist[i][j]/sum[i];
}
void work()
{
    if(T!=0)
    {
        pp.x=pp.y=N;
        for(int i=1;i<=N;i++)
        for(int j=1;j<=N;j++)
            pp.A[i][j]=p[j][i];
        tmp=re=pp;
        for(int j=1;(1<<j)<=T;j++)
        {
            if((1<<j)&T) re=re*tmp;
            tmp=tmp*tmp;
        }
        ans.x=N,ans.y=1;
        for(int i=1;i<=N;i++)
            ans.A[i][1]=f[i];
        ans=re*ans;
        for(int i=1;i<=N;i++)
            printf("%.6lf\n",ans.A[i][1]);  
    }
    else
    {
        for(int i=1;i<=N;i++)
            printf("%.6lf\n",f[i]);
    }
}
int main()
{
    freopen("robbers.in","r",stdin);
    freopen("robbers.out","w",stdout);
    data_in();
    ready();
    work();
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章