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

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

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

题意

给定长度为 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 ) + ∑ j + k = i D P [ 0 ] [ 1 ] ( j ) × D P [ 0 ] [ 0 ] ( k ) + ∑ j + k = i D P [ 0 ] [ 0 ] ( j ) × D P [ 1 ] [ 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)+\sum_{j+k=i} DP[0][1](j) \times DP[0][0] (k)+\sum_{j+k=i} DP[0][0](j) \times DP[1][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)+j+k=iDP[0][1](j)×DP[0][0](k)+j+k=iDP[0][0](j)×DP[1][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 就是了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值