20240808题解

话说T2写了个动态树结果考场调不出来,这下大炮打蚊子了。

森林 (forest)

题面:
符合条件的森林中深度相同的点度数相同,\(1\le i\le n\),求点数为\(i\)的符合条件的森林的种类数。
题解:
将森林中每一个根节点连到同一个节点上变成一棵树。令\(f(i)\)表示符合条件的树的种类数,那么\(f(i+1)\)\(i\)的答案。
因为一个\(i\)个节点的树去掉根节点后一定能分成多个相同的子树,若子树大小为\(j\),那么\(j\mid(i-1)\)

\[f(i)=\sum_{j\mid(i-1)}f(j) \]

这种题绝对可以去OEIS上找吧
时间复杂度\(\text O(n\log n)\)
代码:

#include<cstdio>
#define int long long
const int N=1000005,mod=998244353;
int f[N],n,c[20];
void write(int x){
    int top=0;
    for(;x;x/=10)c[++top]=x%10;
    for(;top;top--)putchar(c[top]+'0');
    putchar(' ');
}
inline void Add(int&x,int y){(x+=y)>=mod&&(x-=mod);}
signed main(){
    freopen("forest.in","r",stdin),freopen("forest.out","w",stdout),scanf("%lld",&n),f[1]=1;
    for(int i=1;i<=n;i++)for(int j=i+1;j<=n+1;j+=i)Add(f[j],f[i]);
    for(int i=2;i<=n+1;i++)write(f[i]);
    return fflush(stdout),fclose(stdin),fclose(stdout),0;
}

树上路径 (path)

题面:有一个\(n\)个点的外向树,依次加入\(n-1\)条边,图的中间形态为一个外向树形成的森林,每次查询一个点\(c\),保证\(c\)是一个外向树的梗,求\(c\)的最深的子节点的深度。
题解:带权并查集直接维护即可。
不知道自己怎么会想到写LCT。
时间复杂度\(\text O(n\log n)\)
代码:

#include<cstdio>
const int N=1000005;
int T,n,fa[N],dep[N],res[N];
struct Edge{
    int to,next;
}edge[N];
inline void Max(int&x,int y){x<y&&(x=y);}
inline int find(int x){
    if(x==fa[x])return x;
    int t=find(fa[x]);
    dep[x]+=dep[fa[x]];
    return fa[x]=t;
}
void merge(int x,int y){
    fa[y]=x,dep[y]=1,find(y),Max(res[find(x)],res[y]+dep[y]);
}
int main(){
    for(freopen("path.in","r",stdin),freopen("path.out","w",stdout),scanf("%d",&T);T--;puts("")){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)fa[i]=i,dep[i]=res[i]=0;
        for(int i=1,u,v,c;i<n;i++)scanf("%d%d%d",&u,&v,&c),merge(u,v),printf("%d ",res[c]);
    }
    return fflush(stdout),fclose(stdin),fclose(stdout),0;
}

打字机 (type)

题面:有\(n\)个长度为\(m\)的01串,将这\(n\)个串以任意顺序组合出的长度为\(nm\)的串为好的串。现在可以从头到尾在每一位填\(0\)\(1\),成功率为\(p\),失败会打出另一种字符。求最优策略下打出好的01串的概率(可以根据前面已经打的字符决定策略)。
提接:将\(n\)个串建立trie树,好的字符串就是每次到叶子节点就回到根节点,而且经历了\(n\)个不同的叶子节点的方案数。
\(f(i,j)\)表示在某一节点走0的子树有\(i\)个叶子节点,走1的子树有\(j\)个叶子节点的最优方案的概率。

\[f(i,j)=max\{p\times f(i-1,j)+(1-p)\times f(i,j-1),p\times f(i,j-1)+(1-p)\times f(i-1,j)\} \]

最后答案为每个节点对应的\(f(i,j)\)的积。
代码:

#include<cstdio>
const int N=1005;
int n,m,trie[N*N][2],ptrie,cnt[N*N][2];
double f[N][N],p,ans=1;
char x[N];
double max(double x,double y){return x>y?x:y;}
double min(double x,double y){return x<y?x:y;}
inline void ins(char*s){
    int u=0;
    for(int i=1;i<=m;i++){
        int p=s[i]^'0';
        if(!trie[u][p])trie[u][p]=++ptrie;
        cnt[u][p]++,u=trie[u][p];
    }
}
int main(){
    freopen("type.in","r",stdin),freopen("type.out","w",stdout),scanf("%d%d%lf",&n,&m,&p);
    for(int i=1;i<=n;i++)scanf("%s",x+1),ins(x);
    f[0][0]=1;
    for(int i=1;i<=n;i++)f[i][0]=f[i-1][0]*p,f[0][i]=f[0][i-1]*p;
    for(int i=1;i<=n;i++)for(int j=1;i+j<=n;j++)f[i][j]=p*max(f[i-1][j],f[i][j-1])+(1-p)*min(f[i-1][j],f[i][j-1]);
    for(int i=0;i<=n;puts(""),i++)for(int j=0;i+j<=n;j++)printf("%.9lf ",f[i][j]);
    for(int i=0;i<=ptrie;i++)ans*=f[cnt[i][0]][cnt[i][1]];
    printf("%.12f",ans);
    return fflush(stdout),fclose(stdin),fclose(stdout),0;
}
posted @ 2024-08-08 15:59  junjunccc  阅读(3)  评论(0编辑  收藏  举报