• 【2022国赛模拟】[SDWC Day4] 数列——树形DP


    好久没有写题解了喔!这次又没有链接!

    题目描述

    在这里插入图片描述

    题解

    由于 n n n 比较小,我们不妨把我们能直接得到的大小关系用有向边建出来( > > > ≥ \ge 的边作区分),那么可以得到一个 DAG。

    然后我们发现,这个 DAG 上有很多边是没用的。比如有 a > b , a > c , c ≥ b a>b,a>c,c\ge b a>b,a>c,cb,那么此时 a > b a>b a>b 这条边就是没用的。

    我们把所有没用的边去掉后,这个 DAG 一定可以变为一颗内向/外向树,因为它必然是一棵合法笛卡尔树的一部分。此时树上每个节点到祖先的路径必然是原 DAG 上的最长路径,所以可以用 Floyd 求最长路然后辅助判断。

    然后就是一个裸的树形DP了,总复杂度 O ( n 3 + n m ) O(n^3+nm) O(n3+nm)

    代码

    #include//JZM yyds!!
    #define ll long long
    #define lll __int128
    #define uns unsigned
    #define fi first
    #define se second
    #define IF (it->fi)
    #define IS (it->se)
    #define END putchar('\n')
    #define lowbit(x) ((x)&-(x))
    #define inline jzmyyds
    using namespace std;
    const int MAXN=1e5+5;
    const ll INF=1e17;
    ll read(){
    	ll x=0;bool f=1;char s=getchar();
    	while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
    	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
    	return f?x:-x;
    }
    int ptf[50],lpt;
    void print(ll x,char c='\n'){
    	if(x<0)putchar('-'),x=-x;
    	ptf[lpt=1]=x%10;
    	while(x>9)x/=10,ptf[++lpt]=x%10;
    	while(lpt>0)putchar(ptf[lpt--]^48);
    	if(c>0)putchar(c);
    }
    const ll MOD=998244353;
    ll ksm(ll a,ll b,const ll&mo=MOD){
    	ll res=1;
    	for(;b;b>>=1,a=a*a%mo)if(b&1)res=res*a%mo;
    	return res;
    }
    using pii=pair<int,int>;
    int n,m,fa[105],ds[105][105];
    vector<int>G[105],D[105];
    ll dp[105][MAXN],ans=1;
    void addD(int u,int v,bool w){D[u].emplace_back(v<<1|w);}
    void ad(ll&a,const ll&b){a+=b;if(a>=MOD)a-=MOD;}
    void dfs(int x){
    	for(int i=1;i<=m;i++)dp[x][i]=1;
    	for(int pv:G[x]){
    		int v=pv>>1,e=pv&1;
    		dfs(v);
    		ll cg=0;
    		for(int i=1;i<=m;i++)ad(cg,dp[v][i-e]),(dp[x][i]*=cg)%=MOD;
    	}
    }
    int main()
    {
    	freopen("array.in","r",stdin);
    	freopen("array.out","w",stdout);
    	n=read(),m=read();
    	for(int i=1;i<=n;i++){
    		int b=read();
    		if(b<0)continue;
    		if(b)addD(b,i,1);
    		for(int j=b+1;j<i;j++)addD(i,j,0);
    	}memset(ds,-0x3f,sizeof(ds));
    	for(int x=1;x<=n;x++){
    		ds[x][x]=0;
    		for(int pv:D[x])ds[x][pv>>1]=1;
    	}
    	for(int k=1;k<=n;k++)
    		for(int i=1;i<=n;i++)if(i^k)
    			for(int j=1;j<=n;j++)if((i^j)&&(k^j))
    				ds[i][j]=max(ds[i][j],ds[i][k]+ds[k][j]);
    	for(int x=1;x<=n;x++)
    		for(int pv:D[x]){
    			int v=pv>>1;
    			if(ds[x][v]==1)G[x].push_back(pv),fa[v]=x;
    		}
    	for(int x=1;x<=n;x++)if(!fa[x]){
    		dfs(x);ll cg=0;
    		for(int i=1;i<=m;i++)ad(cg,dp[x][i]);
    		(ans*=cg)%=MOD;
    	}
    	print(ans);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    优化

    第一部分的删边,其实有更快的拓扑排序的 O ( n 3 ω ) O(\frac{n^3}{\omega}) O(ωn3) 做法,而第二部分的DP,由于DP值是个 n n n 次函数,所以可以用拉插做到 O ( n 2 ) O(n^2) O(n2)

    所以 n n n 完全可以开到 1000 以上。

  • 相关阅读:
    C中的基本函数
    专访清华裘捷中:亚洲高校首个KDD最佳博士论文奖是如何炼成的?
    javaweb之过滤器与监听器
    .NET Task 揭秘(3)async 与 AsyncMethodBuilder
    python 执行远程shell命令tail并实时输出示例
    如何搭建测试环境?一文解决你所有疑惑!
    力扣刷题记录103.1-----518. 零钱兑换 II
    20李沐动手学深度学习v2/参数管理
    数据结构 第一章作业 绪论 西安石油大学
    seata的部署和集成
  • 原文地址:https://blog.csdn.net/weixin_43960287/article/details/126332601