题目链接

考虑枚举好点的集合。此时要考虑的问题是如何填入\(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. 十四、制作优美的div弹框

    功能描述:确认[调整按钮]弹出精美div弹框 1.jsp页面:perfectAlertDiv.jsp <%@ page contentType="text/html;charset=U ...

  2. js判断ie和edge是否安装Adobe Reader PDF阅读器

    ie浏览器和edge浏览器,必须用Adobe Reader PDF阅读器才可以打开pdf文件,其他现代浏览器自带pdf阅读器,无需安装. 判断ie或者edge如果安装了,就浏览pdf文件:如果没安装就 ...

  3. vagrant up启动centos7时出现"rsync" could not be found on your PATH. Make sure that rsyncis properly ins

    (1)问题1:"rsync" could not be found on your PATH. Make sure that rsyncis properly ins 解决方法: ...

  4. python邮箱发送

    普通发送邮件 使用email模块和stmplib模块,内容比较固定,配好了即可实现,代码如下 一.普通邮箱发送 # -*- coding:utf-8-*- import smtplib from em ...

  5. 如何去掉Eclipse注释中英文单词的拼写错误检查

  6. 《容器化.NET应用架构指南》脑图学习笔记(第一部分)

    一.关于这本官方“圣经” 作为.NET程序员,对于微软官方推动的架构示例总是特别关注,从PetShop到MusicStore再到eShopOnContainers,每一次关注,都会了解到业界最新的架构 ...

  7. R语言 一个向量的值分派给另一个向量

    group = sample(seq(1,10),size = 20,replace = T) #这20个组分别属于1,...,10 v = rnorm(length(unique(group)),0 ...

  8. C# String 字符串一些关键理解

    #1 :在.Net Framework中,字符总是表示成16位Unicode的代码#2 :String 和string 其实是一样的只是表现形式上不同#3 :string类型被视为基元类型,也就是编译 ...

  9. 109、Java中String类之截取部分子字符串

    01.代码如下: package TIANPAN; /** * 此处为文档注释 * * @author 田攀 微信382477247 */ public class TestDemo { public ...

  10. spark aggregateByKey 时 java.lang.OutOfMemoryError: GC overhead limit exceeded

    最后发现有一个用户单日访问我们网站次数为 4千万,直接导致 aggregate 时内存不够.过滤掉该用户即可.