NOIP模擬測試1「co……·記憶的輪廓·雨天的尾巴」

考試的時候用哈希水過了第一題原本想用哈希只能夠得20左右沒想到因爲數據過於水A了html

而後雨天的尾巴騙了5分,總分105 我太菜了c++

首先時間分配的不合理:第一題大水題ac自動機打完了都不會,第二題略微想了想打了個高斯消元,而後樣例沒過......,最後輸出了一個隨機數,第三題(lca板子忘了,打錯一個地方,沒有調出來)最後騙了五分git

 

考後主要講一下第二題:記憶的輪廓(bzoj4899)和第三題:雨天的尾(yi)巴(bzoj3307)數組


 

Censoring

FJ把雜誌上全部的文章摘抄了下來並把它變成了一個長度不超過的字符串S。他有一個包含n個單詞的列表,列表裏的n個單詞記t1 ....tn爲他但願從S中刪除這些單詞。
FJ每次在S中找到最先出現的列表中的單詞(最先出現指該單詞的開始位置最小),而後從S中刪除這個單詞。他重複這個操做直到S中沒有列表裏的單詞爲止。注意刪除一個單詞後可能會致使S中出現另外一個列表中的單詞
FJ注意到列表中的單詞不會出現一個單詞是另外一個單詞子串的狀況,這意味着每一個列表中的單詞在S中出現的開始位置是互不相同的
請幫助FJ完成這些操做並輸出最後的S串1​​...tNt_NtN​​。他但願從S中刪除這些單詞。
FJ每次在S中找到最先出現的列表中的單詞(最先出現指該單詞的開始位置最小),而後從S中刪除這個單詞。他重複這個操做直到S中沒有列表裏的單詞爲止。注意刪除一個單詞後可能會致使S中出現另外一個列表中的單詞
FJ注意到列表中的單詞不會出現一個單詞是另外一個單詞子串的狀況,這意味着每一個列表中的單詞在S中出現的開始位置是互不相同的
請幫助FJ完成這些操做並輸出最後的S
數據結構

沒什麼好講的,ac自動機而後開棧維護ide


 

記憶的輪廓

 

內存限制:512 MiB 時間限制:1000 ms 標準輸入輸出
 
 
 
 
 
 
 

 

 

題目描述

通往賢者之塔的路上,有許多的危機。
咱們能夠把這個地形看作是一顆樹,根節點編號爲1,目標節點編號爲n,其中1-n的簡單路徑上,編號依次遞增,在[1,n]中,一共有n個節點。咱們把編號在[1,n]的叫作正確節點,[n+1,m]的叫作錯誤節點。一個葉子,若是是正確節點則爲正確葉子,不然稱爲錯誤葉子。莎緹拉要幫助昴到達賢者之塔,所以如今面臨着存檔位置設定的問題。
爲了讓昴成長爲英雄,所以一共只有p次存檔的機會,其中1和n必須存檔。被莎緹拉設置爲要存檔的節點稱爲存檔位置。固然不能讓昴陷入死循環,因此存檔只能在正確節點上進行,並且同一個節點不能存屢次檔。由於通往賢者之塔的路上有影響的瘴氣,所以莎緹拉假設昴每次位於樹上一個節點時,都會等機率選擇一個兒子走下去。每當走到一個錯誤葉子時,再走一步就會讀檔。具體的,每次昴到達一個新的存檔位置,存檔點便會更新爲這個位置(假如如今的存檔點是i,如今走到了一個存檔位置j>i,那麼存檔點便會更新爲j)。讀檔的意思就是回到當前存檔點。初始昴位於1,當昴走到正確節點n時,便結束了路程。莎緹拉想知道,最優狀況下,昴結束路程的指望步數是多少?測試

輸入格式

第一行一個正整數T表示數據組數。
接下來每組數據,首先讀入三個正整數n,m,p。
接下來m-n行,描述樹上全部的非正確邊(正確邊即鏈接兩個正確節點的邊)
用兩個正整數j,k表示j與k之間有一條連邊,j和k能夠均爲錯誤節點,也能夠一個爲正確節點另外一個爲錯誤節點。
數據保證j是k的父親。
50<=p<=n<=700,m<=1500,T<=5。
數據保證每一個正確節點均有至少2個兒子,至多3個兒子。ui

輸出格式

T行每行一個實數表示每組數據的答案。請保留四位小數。spa

樣例

樣例輸入

1
3 7 2
1 4
2 5
3 6
3 7

樣例輸出

9.0000

這個題仍是挺有意思的code

題目中提到這一句話
「「咱們能夠把這個地形看作是一顆樹,根節點編號爲1,目標節點編號爲n,其中1-n的簡單路徑上,編號依次遞增,在[1,n]中,一共有n個節點。咱們把編號在[1,n]的叫作正確節點,[n+1,m]的叫作錯誤節點。」」
那麼題目中就暗示了1--n的路徑確定是一條鏈

首先題目中說簡單路徑
其次具體能夠反證出來
好比假設有三個點1-2 1-3 各有一條邊那麼1-3的簡單路徑就只有兩個節點與n(n==3)個節點矛盾因此1-n的路徑必定是一條鏈

這一個小點必定要讀出來

設d[i]爲i的出度,g[i]爲錯誤兒子i返回存檔指望步數,s[i]爲當前點走到全部錯誤節點g之和

設a[i][j]表示以i爲最新存檔點走到j時指望步數,f[i][j]爲以i爲下一個存檔點當前還剩餘j個存檔點

咱們逆着轉移f,f能夠由任意一個在i以後的點而且剩餘存檔數量爲j-1的f貢獻

而後分析 這個dp實際上就是在1-n一條鏈上進行的, 那麼咱們其實就能夠寫出一個」相似「於線性dp的方程式

$f[i][j]=min(f[i][j],f[k][j-1]+a[i][k])$

這裏a數組求法分析

設c是從j-1走到j的指望步數

a[i][j]=a[i][j-1]+c

分析

首先1/d[j-1]機率走到正確節點

其餘能夠由首先走到錯誤節點son

而後返回存檔i,再繼續走一個a[i][j-1],而後再加上從j-1走到j的指望步數

這裏有一個注意點 這裏的c在Σ中要加一(走到錯誤節點要走一步)

因而

$c=1/d[j-1]+Σ(g[son]+a[i][j-1]+c+1)$//        (son表示j-1的錯誤兒子)

分析咱們將全部g相加就是s

$c=(1/d[j-1])+(d[j-1]-1)/d[j-1]+(1/d[j-1])*s[j-1]+((d[j-1]-1)/d[j-1])*a[i][j-1]+((d[j-1]-1)/d[j-1])*c$

移項

$1/d[j-1]*c=1+1/d[j-1]*s[j-1]+((d[j-1]-1)/d[j-1])*a[i][j-1];$

首先要預處理出走到錯誤節點返回的指望,具體能夠經過一個簡單dfs處理

而後咱們再次相乘得出

$c=d[j-1]+s[j-1]+(d[j-1]-1)*a[i][j-1];$

最後相加

得出

$a[i][j]=a[i][j-1]*d[j-1]+d[j-1]+s[j-1];$

而後

$f[i][j]=min(f[i][j],f[k][j-1]+a[i][k]);$

那麼咱們推測對於a數組來講 它的快速增加確定會爆

而後咱們記錄一個step,推測每次轉移大體最大相差40步左右(但我以爲這麼作是qj測試點)

而後就有了if(k-i>40) break;

 通過實際測試(因爲測試點過水) k-i 取到20左右就能夠了

如下是本人醜陋的代碼

#include<bits/stdc++.h>
#define ll long long
#define db double
#define A 2500
using namespace std;
ll t,tot=0,n,m,p,head[A],nxt[A],ver[A];
db g[A],a[A][A],cur,f[A][A],d[A],s[A];
bool flag[A];
inline void add(ll x,ll y)
{nxt[++tot]=head[x];head[x]=tot;ver[tot]=y;}
inline ll read()
{
    ll x=0,f=1;char c=getchar();
    while(!isdigit(c))
    {
        if(c=='-')
        f=-1;
        c=getchar();
    }
    while(isdigit(c))
    {
        x=(x<<1)+(x<<3)+c-'0';
        c=getchar();
    }
    return f*x;
}
void dfs(ll x)
{
    flag[x]=1;
    g[x]=1.0;
    for(ll i=head[x];i;i=nxt[i])
    {
        ll y=ver[i];
        if(!flag[y])
            dfs(y);
        g[x]+=1.0/d[x]*g[y];
    }
}
int main()
{
    t=read();
    while(t--)
    {    
        tot=0;
        memset(flag,0,sizeof(flag));
        memset(head,0,sizeof(head));
        memset(nxt,0,sizeof(nxt));
        memset(ver,0,sizeof(ver));
        memset(d,0,sizeof(d));
        memset(g,0,sizeof(g));
        memset(flag,0,sizeof(flag));
        memset(f,125,sizeof(f));
        n=read();m=read();p=read();
        for(ll i=1;i<=m-n;i++)
        {
            ll x,y;
            x=read(),y=read();
            add(x,y);d[x]++;
        }
        for(ll i=1;i<n;i++)
            d[i]++;
        for(ll i=1;i<=n;i++)
            if(!flag[i])dfs(i);
        for(ll i=1;i<=n;i++)
        {
            s[i]=0;
            for(ll j=head[i];j;j=nxt[j])
            {
                ll y=ver[j];
                s[i]+=g[y];
            }
        }
        for(ll i=1;i<=n;i++)
        {    
            a[i][i]=0;
            for(ll j=i+1;j<=n;j++)
                a[i][j]=a[i][j-1]*d[j-1]+s[j-1]+d[j-1];
        }    
        f[n][1]=0.0;    
        for(ll j=2;j<=p;j++)
        {
            for(ll i=1;i<=n;i++)
                for(ll k=i+1;k<=n;k++)
                {
                    if(k-i>40) break;
                    f[i][j]=min(f[i][j],f[k][j-1]+a[i][k]);
                }
        }
        db ans=f[1][p];
        printf("%.4lf\n",ans);    
    }
    return 0;
}
View Code

 

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 #define db double
 4 #define A 2500
 5 using namespace std;
 6 ll t,tot=0,n,m,p,head[A],nxt[A],ver[A];
 7 db g[A],a[A][A],cur,f[A][A],d[A],s[A];
 8 bool flag[A];
 9 inline void add(ll x,ll y)
10 {nxt[++tot]=head[x];head[x]=tot;ver[tot]=y;}
11 inline ll read()
12 {
13     ll x=0,f=1;char c=getchar();
14     while(!isdigit(c))
15     {
16         if(c=='-')
17         f=-1;
18         c=getchar();
19     }
20     while(isdigit(c))
21     {
22         x=(x<<1)+(x<<3)+c-'0';
23         c=getchar();
24     }
25     return f*x;
26 }
27 void dfs(ll x)
28 {
29     flag[x]=1;
30     g[x]=1.0;
31     for(ll i=head[x];i;i=nxt[i])
32     {
33         ll y=ver[i];
34         if(!flag[y])
35             dfs(y);
36         g[x]+=1.0/d[x]*g[y];
37     }
38 }
39 int main()
40 {
41     t=read();
42     while(t--)
43     {    
44         tot=0;
45         memset(flag,0,sizeof(flag));
46         memset(head,0,sizeof(head));
47         memset(nxt,0,sizeof(nxt));
48         memset(ver,0,sizeof(ver));
49         memset(d,0,sizeof(d));
50         memset(g,0,sizeof(g));
51         memset(flag,0,sizeof(flag));
52         memset(f,125,sizeof(f));
53         n=read();m=read();p=read();
54         for(ll i=1;i<=m-n;i++)
55         {
56             ll x,y;
57             x=read(),y=read();
58             add(x,y);d[x]++;
59         }
60         for(ll i=1;i<n;i++)
61             d[i]++;
62         for(ll i=1;i<=n;i++)
63             if(!flag[i])dfs(i);
64         for(ll i=1;i<=n;i++)
65         {
66             s[i]=0;
67             for(ll j=head[i];j;j=nxt[j])
68             {
69                 ll y=ver[j];
70                 s[i]+=g[y];
71             }
72         }
73         for(ll i=1;i<=n;i++)
74         {    
75             a[i][i]=0;
76             for(ll j=i+1;j<=n;j++)
77                 a[i][j]=a[i][j-1]*d[j-1]+s[j-1]+d[j-1];
78         }    
79         f[n][1]=0.0;    
80         for(ll j=2;j<=p;j++)
81             for(ll i=1;i<=n;i++)
82                 for(ll k=i+1;k<=n;k++)
83                 {
84                     if(k-i>40) break;
85                     f[i][j]=min(f[i][j],f[k][j-1]+a[i][k]);
86                 }
87         db ans=f[1][p];
88         printf("%.4lf\n",ans);    
89     }
90     return 

 

C. 雨天的尾巴

內存限制:128 MiB 時間限制:1000 ms 標準輸入輸出
 
 
 
 
 
 
 
 
 
 

題目描述

N個點,造成一個樹狀結構。有M次發放,每次選擇兩個點x,y對於x到y的路徑上(含x,y)每一個點發一袋Z類型的物品。完成全部發放後,每一個點存放最多的是哪一種物品。

輸入格式

第一行數字N,M
接下來N-1行,每行兩個數字a,b,表示a與b間有一條邊
再接下來M行,每行三個數字x,y,z.如題

輸出格式

輸出有N行
每i行的數字表示第i個點存放最多的物品是哪種,若是有
多種物品的數量同樣,輸出編號最小的。若是某個點沒有物品則輸出0

樣例

樣例輸入

20 50
8 6
10 6
18 6
20 10
7 20
2 18
19 8
1 6
14 20
16 10
13 19
3 14
17 18
11 19
4 11
15 14
5 18
9 10
12 15
11 14 87
12 1 87
14 3 84
17 2 36
6 5 93
17 6 87
10 14 93
5 16 78
6 15 93
15 5 16
11 8 50
17 19 50
5 4 87
15 20 78
1 17 50
20 13 87
7 15 22
16 11 94
19 8 87
18 3 93
13 13 87
2 1 87
2 6 22
5 20 84
10 12 93
18 12 87
16 10 93
8 17 93
14 7 36
7 4 22
5 9 87
13 10 16
20 11 50
9 16 84
10 17 16
19 6 87
12 2 36
20 9 94
9 2 84
14 1 94
5 5 94
8 17 16
12 8 36
20 17 78
12 18 50
16 8 94
2 19 36
10 18 36
14 19 50
4 12 50

樣例輸出

87
36
84
22
87
87
22
50
84
87
50
36
87
93
36
94
16
87
50
50

數據範圍與提示

1<=N,M<=100000
1<=a,b,x,y<=N
1<=z<=10910^9109​​

也是一道不錯的題

思考咱們若是按照既定套路,進行樹上拆分的話

咱們還須要維護每個節點出現的值中最大的是什麼  

咱們首先能夠想到開一個v[n][max_size]數組

而後要i至j的z值+1就

v[i][z]++,v[j][z]++,v[lca][z]--,v[f[lca][0]][z]--;

若是隻是簡單開數組會MLE+TLE

爲了省時間+省空間

咱們能夠創建n棵權值線段樹

若是咱們不舉行別的措施仍然會MLE,故使用動態開點線段樹

另外因爲值特別大而咱們開的是權值線段樹,咱們能夠進行離散化也能夠採起」別的操做「

別的操做:具體來講例若有m組詢問,咱們值域只須要開到m    其實仍是和離散化差很少

最後統一進行線段樹合併

完了(數據結構題真不知道要說些什麼)

如下依然是本人醜陋的代碼

#include<bits/stdc++.h>
#define A 100005
#define ll int
using namespace std;
bool flag[A*2];
ll cnt=0,Ans[A*60],ans[A*60],deep[A*2],f[A][24],head[A*2],next[A*2],ver[A*2],lc[A*60],rc[A*60],tot=0,T[A*60];
ll n,m,sum[A];
inline ll read()
{
    ll f=1,x=0;char c=getchar();
    while(!isdigit(c))
    {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(isdigit(c))
    {
        x=x*10+c-'0';
        c=getchar();
    }
    return f*x;
}
void pushup(ll now)
{
    if(ans[lc[now]]>=ans[rc[now]])
    ans[now]=ans[lc[now]],Ans[now]=Ans[lc[now]];
    else
    ans[now]=ans[rc[now]],Ans[now]=Ans[rc[now]];
}
void add(ll x,ll y)
{
    next[++tot]=head[x];
    head[x]=tot;
    ver[tot]=y;
}
ll merge(ll x,ll y,ll l,ll r)
{
    if(l==r&&x&&y) ans[x]+=ans[y];
    if(!x||!y) return y+x;
    ll mid=(l+r)>>1;
    lc[x]=merge(lc[x],lc[y],l,mid);
    rc[x]=merge(rc[x],rc[y],mid+1,r);
    if(l!=r)pushup(x);
    return x;
}
void change(ll &p,ll l,ll r,ll pos,ll v)
{
    if(!p) p=++cnt;
    if(l==r)
    {ans[p]+=v;Ans[p]=l;return ;}
    ll mid=(l+r)>>1;
    if(pos<=mid) change(lc[p],l,mid,pos,v);
    else change(rc[p],mid+1,r,pos,v);
    if(l!=r) pushup(p);
}
void dfs(ll x,ll dep)
{
    flag[x]=1,deep[x]=dep;
    for(ll i=head[x];i;i=next[i])
    {
        ll y=ver[i];
        if(flag[y]) continue;
        f[y][0]=x;
        deep[y]=dep;
        dfs(y,dep+1);
    }
}
ll lca(ll x,ll y)
{
    if(deep[x]>deep[y])
        swap(x,y);
    for(ll i=20;i>=0;i--)
    {
        if(deep[x]<=deep[f[y][i]])
            y=f[y][i];
        if(deep[x]==deep[y]) break;
    }
    if(x==y) return x;
    for(ll i=20;i>=0;i--)
    {
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    }
    return f[x][0];
}
void dfs_(ll x)
{
    flag[x]=1;
    for(ll i=head[x];i;i=next[i])
    {
        ll y=ver[i];
        if(flag[y]) continue;
        dfs_(y);
        T[x]=merge(T[x],T[y],1,A);
    }
    if(ans[T[x]]>0)
        sum[x]=Ans[T[x]];
}
int main()
{
    n=read(),m=read();
    for(ll i=1;i<n;i++)
    {
        ll xx=read(),yy=read();
        add(xx,yy);
        add(yy,xx);
    }
    dfs(1,1);
    f[1][0]=0;
    for(ll i=1;i<=20;i++)
        for(ll j=1;j<=n;j++)
            f[j][i]=f[f[j][i-1]][i-1];
    ll LCA;
    for(ll i=1;i<=m;i++)
    {
        ll x=read(),y=read(),z=read();
        LCA=lca(x,y);
        change(T[x],1,A,z,1);
        change(T[y],1,A,z,1);
        change(T[LCA],1,A,z,-1);
        change(T[f[LCA][0]],1,A,z,-1);
    }
    memset(flag,0,sizeof(flag));
    dfs_(1);
    for(ll i=1;i<=n;i++)
        printf("%d\n",sum[i]);
}
View Code
相關文章
相關標籤/搜索