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
相关文章
相关标签/搜索