题目链接

考虑枚举好点的集合。此时要考虑的问题是如何填入\(1\sim n\)这些数使得恰好我们枚举到的这些点是好点,即:求出有多少种合法的填数方案。

\(1\)号点一定是好点。那么除\(1\)号点外每个点的祖先中必有至少一个好点。定义\(u\)的祖先中离\(u\)最近的好点,为\(u\)的祖先好点\((2\leq u\leq n)\)。

对除\(1\)外的每个点:

  • 如果它是好点,我们就从它的祖先好点向它连一条边。(称为“第一类边”)

  • 如果它是不是好点,就从它向它的祖先好点连一条边。(称为“第二类边”)

那么对于任意一条边\(a\rightarrow b\),它代表的含义是一种限制:\(val_a<val_b\)。显然,一种填数方案合法,当且仅当满足所有这些限制。

这些边的方向有上有下,让我们非常头疼。如果只有一些第一类限制,问题相当于给定一棵树,求有多少种填数方案使得祖先的权值总是小于后代。这是非常简单的经典问题(其答案是\(n!\prod_u\frac{1}{sz_u}\))。

所以我们可以考虑对第二类边做容斥。即:我们强行把一些坏点向其祖先好点连的边的方向反过来,求出此时的填数方案数,再乘以\((-1)^{\text{被反向的坏点个数}}\),加入答案。当然,我们不能暴力枚举使哪些坏点反向,否则总复杂度变为\(O(3^n\times n)\),且没什么优化前途。

考虑DP。设\(dp[u][i]\)表示考虑了\(u\)的子树,共选出\(i\)个有限制的点(既包括所有原本就是好点的点,也包括一些被容斥强行反向的坏点)的填数方案数。注意:
这里的填数方案只考虑了这\(i\)个有限制的点,对于无限制的坏点我们统计答案时再计算。并且对于这\(i\)个有限制的点,我们只考虑它们填的数的相对大小关系,这样方便转移。

把\(u\)的子树的方案合并起来,就是做一个简单的树上背包。也就是大家所熟知的那个看起来是\(O(n^3)\)实际上是\(O(n^2)\)的算法。这里不再赘述。

设我们把\(u\)的所有儿子的DP值合并得到\(g\),即:在\(u\)的所有儿子子树内,选出\(i\)个有限制的点的方案数为\(g[i]\)。

然后转移,考虑\(u\)。

  • 若\(u\)是好点。则\(u\)上填的数必是\(i\)个点中最小的,因为我们这里只考虑相对大小关系,因此\(u\)上一定填\(1\)。故\(dp[u][i]=g[i-1]\)。

  • 若\(u\)不是好点。

    • 若\(u\)被选为有限制的点(即\(u\)向其祖先好点连的边被在容斥中反向了):则\(u\)上可以填任意数(因为\(u\)有祖先好点,这个祖先好点在转移时只能填\(1\),所以它的值必然比\(u\)小),所以\(u\)有\(i\)种填法。再乘上容斥系数。所以我们得到:\(dp[u][i]=(-1)\times i\times g[i-1]\)。

    • 若\(u\)没有被选为有限制的点,则\(dp[u][i]=g[i]\)。

    综上所述:\(dp[u][i]=(-1)\times i\times g[i-1]+g[i]\)。

考虑统计在当前好点集合下的方案数。

设好点集合大小为\(c\)。枚举选出了\(i\)个有限制的点。则方案数为:

\[
x=\sum_{i=c}^{n}dp[1][i]\times{n\choose i}\times(n-i)!
\]

我们把这个方案数贡献到答案里,即\(ans+=x\times k^c\)。

至此,我们实现了\(O(2^n\times n^2)\)的复杂度并喜提\(0\)分。但其实我们离正解只有一步之遥。

我们完全可以不枚举好点集合,在DP转移时讨论\(u\)是不是好点即可。至于\(k^c\)这个东西,我们在转移时,如果认为\(u\)是好点,就把它的DP值乘以一个\(k\)。即:\(dp[u][i]=k\times g[i-1]\)。

时间复杂度\(O(n^2)\)。

参考代码:

//problem:nflsoj489
#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fst first
#define scd second

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;

namespace Fread{
const int MAXN=1<<20;
char buf[MAXN],*S,*T;
inline char getchar(){
    if(S==T){
        T=(S=buf)+fread(buf,1,MAXN,stdin);
        if(S==T)return EOF;
    }
    return *S++;
}
}//namespace Fread
#ifdef ONLINE_JUDGE
    #define getchar Fread::getchar
#endif
inline int read(){
    int f=1,x=0;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline ll readll(){
    ll f=1,x=0;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
/*  ------  by:duyi  ------  */ // dysyn1314
const int MAXN=5000,MOD=998244353;
inline int mod1(int x){return x<MOD?x:x-MOD;}
inline int mod2(int x){return x<0?x+MOD:x;}
inline void add(int &x,int y){x=mod1(x+y);}
inline void sub(int &x,int y){x=mod2(x-y);}
inline int pow_mod(int x,int i){int y=1;while(i){if(i&1)y=(ll)y*x%MOD;x=(ll)x*x%MOD;i>>=1;}return y;}
int n,K,fac[MAXN+5],invf[MAXN+5],sz[MAXN+5],dp[MAXN+5][MAXN+5],tmp[MAXN+5];
vector<int>G[MAXN+5];
inline int comb(int n,int k){
    if(n<k)return 0;
    else return (ll)fac[n]*invf[k]%MOD*invf[n-k]%MOD;
}
void dfs(int u,int fa){
    sz[u]=1;
    dp[u][0]=1;
    for(int i=0;i<(int)G[u].size();++i){
        int v=G[u][i];
        if(v==fa)continue;
        dfs(v,u);
        for(int j=0;j<=sz[v]+sz[u];++j)tmp[j]=0;
        for(int j=0;j<=sz[v];++j){
            for(int k=0;k<=sz[u];++k){
                add(tmp[j+k],(ll)dp[v][j]*dp[u][k]%MOD*comb(j+k,j)%MOD);
            }
        }
        sz[u]+=sz[v];
        for(int j=0;j<=sz[u];++j)dp[u][j]=tmp[j];
    }
    for(int i=0;i<=sz[u];++i)tmp[i]=dp[u][i];
    for(int i=1;i<=sz[u];++i){
        dp[u][i]=0;
        add(dp[u][i],(ll)K*tmp[i-1]%MOD);//好点
        if(u!=1){
            sub(dp[u][i],(ll)i*tmp[i-1]%MOD);//假好点(边被反向了的坏点)
            add(dp[u][i],tmp[i]);//u被孤立了
        }
    }
}
int main() {
    n=read();K=read();
    fac[0]=1;for(int i=1;i<=n;++i)fac[i]=(ll)fac[i-1]*i%MOD;
    invf[n]=pow_mod(fac[n],MOD-2);
    for(int i=n-1;i>=0;--i)invf[i]=(ll)invf[i+1]*(i+1)%MOD;
    for(int i=1,u,v;i<n;++i)u=read(),v=read(),G[u].pb(v),G[v].pb(u);
    dfs(1,0);
    int ans=0;
    for(int i=n,s=1;i>=1;s=(ll)s*i%MOD,--i){
        add(ans,(ll)dp[1][i]*s%MOD);
    }
    cout<<ans<<endl;
    return 0;
}

题解 nflsoj489 【六校联合训练 CSP #15】小D与随机的更多相关文章

  1. NFLSOJ 1072 - 【2021 六校联合训练 NOIP #1】异或(FWT+插值)

    题面传送门 一道非常不错的 FWT+插值的题 %%%%%%%%%%%% 还是那句话,反正非六校的看不到题对吧((( 方便起见在下文中设 \(n=2^d\). 首先很明显的一点是这题涉及两个维度:异或和 ...

  2. NFLSOJ 1060 - 【2021 六校联合训练 NOI #40】白玉楼今天的饭(子集 ln)

    由于 NFLSOJ 题面上啥也没有就把题意贴这儿了( 没事儿,反正是上赛季的题,你们非六校学生看了就看了,况且看了你们也没地方交就是了 题意: 给你一张 \(n\) 个点 \(m\) 条边的图 \(G ...

  3. 题解 nflsoj553 【六校联合训练 省选 #10】飞

    题目链接 我们称"简要题意"给出的三个要求分别为"条件1","条件2","条件3". 条件3长得比较丑,考虑转化一下.把 ...

  4. 题解 nflsoj550 【六校联合训练 省选 #9】序列

    题目链接 以下把值域(题面里的\(lim\))记做\(m\). 考虑求\(k\)的答案.考虑每个位置对答案的贡献,枚举位置\(i\),再枚举\(a[i]\)的值\(x\).设: \[ F(k)=\su ...

  5. HDU 5358(2015多校联合训练赛第六场1006) First One (区间合并+常数优化)

    pid=5358">HDU 5358 题意: 求∑​i=1​n​​∑​j=i​n​​(⌊log​2​​S(i,j)⌋+1)∗(i+j). 思路: S(i,j) < 10^10 & ...

  6. 2017多校联合训练2—HDU6054--Is Derek lying?(思维题)

    Is Derek lying? Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)T ...

  7. 2017ACM暑期多校联合训练 - Team 2 1006 HDU 6050 Funny Function (找规律 矩阵快速幂)

    题目链接 Problem Description Function Fx,ysatisfies: For given integers N and M,calculate Fm,1 modulo 1e ...

  8. hdu 4649 Professor Tian 多校联合训练的题

    这题起初没读懂题意,悲剧啊,然后看了题解写完就AC了 题意是给一个N,然后给N+1个整数 接着给N个操作符(只有三种操作  即  或 ,与 ,和异或 |   &  ^ )这样依次把操作符插入整 ...

  9. HDU 4643 GSM 暑期多校联合训练第五场 1001

    点击打开链接 我就不说官方题解有多坑了 V图那么高端的玩意儿 被精度坑粗翔了 AC前 AC后 简直不敢相信 只能怪自己没注意题目For the distance d1 and d2, if fabs( ...

随机推荐

  1. SpringBoot学习笔记(三)——Springboot配置文件

    SpringBoot不像之前用spring+springMVC做项目的时候,他不需要配置大量的看上去很乱很复杂的xml配置文件.在SpringBoot中你可以通过java代码和注解配置项目,也可以通过 ...

  2. The Captain 题解

    20200216题目题解 这是一篇题解祭题解记,但一共就一道题目.(ROS菜大了) 题目如下: The Captain 给定平面上的n个点,定义(x1,y1)到(x2,y2)的费用为min(|x1-x ...

  3. Servlet返回的数据js解析问题

    Servlet返回的数据js解析问题 方式1:Json 接收函数:ajax.responseText后面没括号 其实在之前所说的ajax中还遗留了一些问题就是,Servlet返回给js的数据是如何被j ...

  4. java集合体系结构总结

    好,首先我们根据这张集合体系图来慢慢分析.大到顶层接口,小到具体实现类. 首先,我想说为什么要用集合?简单的说:数组长度固定,且是同种数据类型.不能满足需求.所以我们引入集合(容器)来存储任意数据类型 ...

  5. JavaScript - String对象,字符串,String包装类型

    1. 字符串 1.1 字符串的不可变性 var str = 'abc'; str = 'hello'; // 当重新给str赋值的时候,常量'abc'不会被修改,依然在内存中 // 重新给字符串赋值, ...

  6. Practical aspects of deep learning

    If your Neural Network model seems to have high variance, what of the following would be promising t ...

  7. KVM的客户机可以使用的存储

    KVM的虚拟机可以直接使用宿主机器内的存储设备,比如可以把宿主机器内的硬盘直接暴露给 虚拟机挂载使用 -hda /dev/sfa(宿主机的设备文件) 还可以把镜像文件挂接到虚拟机,作为虚拟机的存储设备 ...

  8. 十 Spring的AOP的底层实现:JDK动态代理和Cglib动态代理

    SpringAOP底层的实现原理: JDK动态代理:只能对实现了接口的类产生代理.(实现接口默认JDK动态代理,底层自动切换) Cglib动态代理(类似Javassist第三方的代理技术):对没有实现 ...

  9. Python学习笔记011

    多行注释 '''字符串 ''' 除了用来多行注释还可以用来打印多行

  10. MNIST手写数字分类simple版(03-2)

    simple版本nn模型 训练手写数字处理 MNIST_data数据   百度网盘链接:https://pan.baidu.com/s/19lhmrts-vz0-w5wv2A97gg 提取码:cgnx ...