To_Heart—题解——「NOI国赛 Round9」划分

news/2025/1/31 3:39:28/

大概的话就是晚鸢生完的。

可能还有的问题是对形式化题意不敏感?不知道了。

题意

给定长度为 2n 的两个序列 A,B,要求构造一个长度为 2n 的序列 C, C i C_i Ci = A i A_i Ai B i B_i Bi 。要求 C 单调不减。求 C 的方案数。

题解

因为部分分的做法对于正解并没有启发,所以就直接开始吧!

考虑分治。假设当前考虑到了区间 [l,r]。

设 dp[0/1][0/1][x] 选择 A/B[l] 和 A/B[r] 以及现在选择在 A 中选择了 x 个。

然后发现对于区间 [l,r] 通过 [l+mid],[mid+1+r] 转移的形式与多项式乘法类似,考虑多项式。发现可以把 dp[0/1][0/1] 看成是关于 x 的多项式。

剩下的就是转移。

D P [ 0 ] [ 0 ] ( i ) = ∑ j + k = i D P [ 0 ] [ 1 ] ( j ) × D P [ 1 ] [ 0 ] ( k ) + ∑ j + k = i D P [ 0 ] [ 0 ] ( j ) × D P [ 0 ] [ 0 ] ( k ) DP[0][0] (i) = \sum_{j+k=i} DP[0][1](j) \times DP[1][0](k) +\sum_{j+k=i} DP[0][0](j) \times DP[0][0] (k) DP[0][0](i)=j+k=iDP[0][1](j)×DP[1][0](k)+j+k=iDP[0][0](j)×DP[0][0](k)

其他的转移应该类似?

但是发现自己没有处理单调的限制。发现只要判断 A/B[mid] 和 A/B[mid+1] 的单调性是否满足就好了。(也许这才是解题重点?)

代码如下:

#include<bits/stdc++.h>
using namespace std;
#define db double
const db PI=acos(-1);
#define ll long long
#define int ll
const ll G=3,Gi=332748118; 
const ll Mod=998244353;int n,m;
ll a[4000005],b[4000005];
int r[4000005];ll Pow(ll a,ll b){ll ans=1;while(b){if(b&1) ans*=a,ans%=Mod;b>>=1,a*=a,a%=Mod;}return ans;
}void NTT(ll *a,int n,int op){for(int i=0;i<n;i++) if(i<r[i]) swap(a[i],a[r[i]]);for(int len=1;len<n;len<<=1){ll W=Pow(op==1?G:Gi,(Mod-1)/(len<<1));for(int i=0;i<n;i+=(len<<1)){ll w=1;for(int j=0;j<len;j++,w=w*W%Mod){ll x=a[i+j],y=w*a[i+j+len]%Mod;a[i+j]=(x+y)%Mod,a[i+j+len]=(x-y+Mod)%Mod;}}}if(op==-1){ll invn=Pow(n,Mod-2);for(int i=0;i<n;i++)a[i]=a[i]*invn%Mod;}
}void Solve(int l,int r,ll *dp00,ll *dp01,ll *dp10,ll *dp11){if(l==r) return dp00[0]=1,dp11[1]=1,void();int mid=(l+r)>>1;int len=(r-l+1);int Max=1,qwq=0;while(Max<len+1) Max<<=1,qwq++;ll l_dp00[Max],l_dp01[Max],l_dp10[Max],l_dp11[Max];ll r_dp00[Max],r_dp01[Max],r_dp10[Max],r_dp11[Max];memset(l_dp00,0,sizeof l_dp00);memset(l_dp01,0,sizeof l_dp01);memset(l_dp10,0,sizeof l_dp10);memset(l_dp11,0,sizeof l_dp11);memset(r_dp00,0,sizeof r_dp00);memset(r_dp01,0,sizeof r_dp01);memset(r_dp10,0,sizeof r_dp10);memset(r_dp11,0,sizeof r_dp11);Solve(l,mid,l_dp00,l_dp01,l_dp10,l_dp11);Solve(mid+1,r,r_dp00,r_dp01,r_dp10,r_dp11);for(int i=0;i<Max;i++) ::r[i]=(::r[i>>1]>>1)|((i&1)<<(qwq-1));NTT(l_dp00,Max,1);NTT(l_dp01,Max,1);NTT(l_dp10,Max,1);NTT(l_dp11,Max,1);NTT(r_dp00,Max,1);NTT(r_dp01,Max,1);NTT(r_dp10,Max,1);NTT(r_dp11,Max,1);if(a[mid]<=a[mid+1]){for(int i=0;i<Max;i++) dp00[i]+=l_dp00[i]*r_dp00[i]%Mod,dp00[i]%=Mod,dp11[i]+=l_dp10[i]*r_dp01[i]%Mod,dp11[i]%=Mod,dp01[i]+=l_dp00[i]*r_dp01[i]%Mod,dp01[i]%=Mod,dp10[i]+=l_dp10[i]*r_dp00[i]%Mod,dp10[i]%=Mod;}if(a[mid]<=b[mid+1]){for(int i=0;i<Max;i++) dp00[i]+=l_dp00[i]*r_dp10[i]%Mod,dp00[i]%=Mod,dp11[i]+=l_dp10[i]*r_dp11[i]%Mod,dp11[i]%=Mod,dp01[i]+=l_dp00[i]*r_dp11[i]%Mod,dp01[i]%=Mod,dp10[i]+=l_dp10[i]*r_dp10[i]%Mod,dp10[i]%=Mod;}if(b[mid]<=a[mid+1]){for(int i=0;i<Max;i++) dp00[i]+=l_dp01[i]*r_dp00[i]%Mod,dp00[i]%=Mod,dp11[i]+=l_dp11[i]*r_dp01[i]%Mod,dp11[i]%=Mod,dp01[i]+=l_dp01[i]*r_dp01[i]%Mod,dp01[i]%=Mod,dp10[i]+=l_dp11[i]*r_dp00[i]%Mod,dp10[i]%=Mod;}if(b[mid]<=b[mid+1]){for(int i=0;i<Max;i++) dp00[i]+=l_dp01[i]*r_dp10[i]%Mod,dp00[i]%=Mod,dp11[i]+=l_dp11[i]*r_dp11[i]%Mod,dp11[i]%=Mod,dp01[i]+=l_dp01[i]*r_dp11[i]%Mod,dp01[i]%=Mod,dp10[i]+=l_dp11[i]*r_dp10[i]%Mod,dp10[i]%=Mod;}NTT(dp00,Max,-1);NTT(dp01,Max,-1);NTT(dp10,Max,-1);NTT(dp11,Max,-1);
}int sub;
ll DP00[2000005],DP11[2000005],DP01[2000005],DP10[2000005];signed main(){freopen("divide.in","r",stdin);freopen("divide.out","w",stdout);cin>>n>>sub;for(int i=1;i<=2*n;i++) scanf("%lld",&a[i]);for(int i=1;i<=2*n;i++) scanf("%lld",&b[i]);Solve(1,n*2,DP00,DP01,DP10,DP11);printf("%lld\n",(DP00[n]+DP01[n]+DP10[n]+DP11[n])%Mod);
}

总结一下就是:

  1. 带有单调性限制的dp可以通过分治减少转移判断次数优化复杂度
  2. 分治后FFT很有用!

但是这道题的视线应该是 NTT 就是了。


http://www.ppmy.cn/news/94996.html

相关文章

数值计算 - 习题复习

1⃣️求矩阵的计算量 2⃣️求相对误差限 3⃣️根据最大误差求绝对误差限和相对误差限 ⚠️为什么使用微分&#xff1f; 绝对误差通过微分近似来计算基于这样的理念&#xff1a;如果我们在某个点附近考虑一个函数&#xff0c;那么该函数在该点附近的行为可以通过该点的切线来近似…

(八)K8S数据持久化存储

1.Volume讲解 概述&#xff1a; Volume是用于存储容器数据的抽象概念&#xff0c;它可以被挂载到一个或多个Pod中的一个或多个容器中。Volume提供了一种持久性的存储方式&#xff0c;使得容器中的数据可以在容器重启、重新调度或迁移时得以保留。 Kubernetes提供了多种类型的…

P1249 乘积最大

最大乘积 题目描述 一个正整数一般可以分为几个互不相同的自然数的和&#xff0c;如 3 1 2 312 312&#xff0c; 4 1 3 413 413&#xff0c; 5 &#xff1d; 1 4 2 3 5&#xff1d;1423 5&#xff1d;1423&#xff0c; 6 1 5 &#xff1d; 2 4 615&#xff1d;24 …

Altium Designer 相同电路多组复制布线

在进行设计开发的时候&#xff0c;总会遇到相同的电路&#xff0c;或者模块&#xff0c;这些电路可以使用相同的布局和走线。我们可以画好其中一部分&#xff0c;然后直接复制&#xff0c;就可以提高效率。下面记录我自己的实际操作过程&#xff0c;有一些地方遇到了问题&#…

首发Yolov8涨点神器:华为诺亚2023极简的神经网络模型 VanillaNet---VanillaBlock助力检测,实现暴力涨点

强烈推荐:博主多个数据集亲测有效,实现暴力涨点,可快速迁移到论文创新性十足,刚新鲜出炉的论文,华为诺亚共同提出!!! 1.VanillaNet 论文:https://arxiv.org/pdf/2305.12972.pdf 来自华为诺亚、悉尼大学的研究者们提出了一种极简的神经网络模型 VanillaNet,以极简主义…

最小二乘估计心得

基本思想 存在一组观察值 ( x i , y i ) (x_i, y_i) (xi​,yi​)&#xff0c;其中 y i y_i yi​和 x i x_i xi​之间满足一定的线性关系&#xff0c;如 y a 0 f 0 ( x ) a 1 f 1 ( x ) . . . a m − 1 f m − 1 ( x ) y a_0 f_0(x) a_1 f_1(x) ... a_{m-1} f_{m-1}(x…

线段树C++详细讲解和个人见解

问题引入 假设有这样的问题&#xff1a;有n个数&#xff0c;m次操作&#xff0c;操作分为&#xff1a;修改某一个数或者查询一段区间的值 数据范围是&#xff08;1 < n, m<1e9)。 这种题大家一看就知道打暴力&#xff0c;但是一看数据范围就知道只能得部分。 我们之前…

GO 语言核心编程-全文版

第 1 章 1.1Golang的学习方向 Go语言&#xff0c;我们可以简单的写成Golang. Golang开山篇 1.2Golang的应用领域 1.2.1区块链的应用开发 1.2.2后台的服务应用 1.2.3云计算/云服务后台应用 1.3学习方法的介绍 1.4讲课的方式的说明 努力做到通俗易懂注重Go语言体系&#xff…