【DP计划】10.26——[UOJ]CTSC2017吉夫特 (Lucas定理+子集DP)“HARD”??

本文探讨了一种利用子集动态规划(DP)解决特定类型数学问题的方法,通过枚举子集并应用Lucas定理,高效计算不上升子序列的数量。文章详细解析了一个具体的编程挑战,展示了如何通过子集DP优化复杂度,实现高效解题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

简单的题目,既是礼物,也是毒药。

B 君设计了一道简单的题目,准备作为 gift 送给大家。

输入一个长度为 n 的数列 a1,a2,⋯,an​ 问有多少个长度大于等于 2 的不上升的子序列满足:

∏i=2k(abi−1abi) mod 2=(ab1ab2)×(ab2ab3)×⋯×(abk−1abk) mod 2>0\prod_{i = 2}^k \binom{a_{b_{i-1}}}{a_{b_i}} \bmod 2= \binom{a_{b_1}}{a_{b_2}} \times \binom{a_{b_2}}{a_{b_3}} \times \cdots \times \binom{a_{b_{k-1}}}{a_{b_k}} \bmod 2 > 0i=2k(abiabi1)mod2=(ab2ab1)×(ab3ab2)××(abkabk1)mod2>0

输出这个个数对 1000000007 取模的结果。

G 君看到题目后,为大家解释了一些基本概念。

我们选择任意多个整数 bi 满足
1≤b1&lt;b2&lt;⋯&lt;bk−1&lt;bk≤n1 \leq b_1 &lt; b_2 &lt; \cdots &lt; b_{k-1} &lt; b_{k} \leq n1b1<b2<<bk1<bkn

我们称 ab1,ab2,…,abk 是 a 的一个子序列。

如果这个子序列同时还满足ab1≥ab2≥⋯≥abk−1≥abka_{b_1} \geq a_{b_2} \geq \cdots \geq a_{b_{k-1}} \geq a_{b_{k}}ab1ab2abk1abk

我们称这个子序列是不上升的。

组合数(nm)\binom{n}{m}(mn) 是从 n 个互不相同的元素中取 m 个元素的方案数,具体计算方法如下:
(nm)=n!m!(n−m)!=n×(n−1)×⋯×2×1(m×(m−1)×⋯×2×1)((n−m)×(n−m−1)×⋯×2×1)\binom{n}{m} = \frac{n!}{m!(n-m)!} = \frac{n \times (n - 1) \times \cdots \times 2 \times 1}{(m \times (m - 1) \times \cdots \times 2 \times 1)((n-m) \times (n-m - 1) \times \cdots \times 2 \times 1)}(mn)=m!(nm)!n!=(m×(m1)××2×1)((nm)×(nm1)××2×1)n×(n1)××2×1

这里要特别注意,因为我们只考虑不上升子序列,所以在求组合数的过程中,一定满足n≥mn \geq mnm也就是(abi−1abi)\binom{a_{b_{i-1}}}{a_{b_{i}}}(abiabi1)中一定有abi−1≥abia_{b_{i-1}} \geq a_{b_{i}}abi1abi

我们在这里强调取模x&VeryThinSpace;mod&VeryThinSpace;yx \bmod yxmody的定义 x&VeryThinSpace;mod&VeryThinSpace;y=x−⌊xy⌋×yx \bmod y = x - \left \lfloor \frac{x}{y} \right \rfloor \times yxmody=xyx×y 其中 ⌊n⌋⌊n⌋n表示小于等于 n 的最大整数。

x&VeryThinSpace;mod&VeryThinSpace;2&gt;0x \bmod 2 &gt; 0xmod2>0就是在说x是奇数。

与此同时,经验告诉我们一个长度为 nn 的序列,子序列个数有O(n2)O(n^2)O(n2)个,所以我们通过对答案取模来避免输出过大。

B 君觉得 G 君说的十分有道理,于是再次强调了这些基本概念。

最后,G 君听说这个题是作为 gift 送给大家,她有一句忠告。

“Vorsicht, Gift!”

“小心……剧毒!”

输入输出格式
输入格式:

第一行一个整数 n。

接下来 n 行,每行一个整数,这 n 行中的第 i 行,表示 ai。

输出格式:

一行一个整数表示答案。

输入输出样例
输入样例#1:

4
15
7
3
1

输出样例#1:

11

说明

• 对于前 10% 的测试点, n ≤ 9, 1 ≤ ai ≤ 13;

• 对于前 20% 的测试点, n ≤ 17, 1 ≤ ai ≤ 20;

• 对于前 40% 的测试点, n ≤ 1911, 1 ≤ ai ≤ 4000;

• 对于前 70% 的测试点, n ≤ 2017;

• 对于前 85% 的测试点, n ≤ 100084;

• 对于 100% 的测试点, 1 ≤ n ≤ 211985, 1 ≤ ai ≤ 233333。所有的 ai 互不相同,也就是说不存在 i, j 同时满足 1 ≤ i < j ≤ n 和 ai = aj。


先找性质,要求满足

∏i=2k(abi−1abi)&VeryThinSpace;mod&VeryThinSpace;2=(ab1ab2)×(ab2ab3)×⋯×(abk−1abk)&VeryThinSpace;mod&VeryThinSpace;2&gt;0\prod_{i = 2}^k \binom{a_{b_{i-1}}}{a_{b_i}} \bmod 2= \binom{a_{b_1}}{a_{b_2}} \times \binom{a_{b_2}}{a_{b_3}} \times \cdots \times \binom{a_{b_{k-1}}}{a_{b_k}} \bmod 2 &gt; 0i=2k(abiabi1)mod2=(ab2ab1)×(ab3ab2)××(abkabk1)mod2>0

如果(abi−1abi)&VeryThinSpace;mod&VeryThinSpace;2=1\binom{a_{b_{i-1}}}{a_{b_i}}\bmod 2=1(abiabi1)mod2=1,那么我们根据Lucas定理,由于(10)=1,(11)=1,(00)=1,(01)=0\binom{1}{0}=1,\binom{1}{1}=1,\binom{0}{0}=1,\binom{0}{1}=0(01)=1,(11)=1,(00)=1,(10)=0,所以不存在(01)\binom{0}{1}(10),那也就是说abi-1&abi=abi-1,也就是说右边的数在左边的数的子集里。
于是就有了做法,子集DP,这道题就是子集DP。这道题就是简单的枚举子集,我们知道枚举子集的复杂度为3n3^n3n。我们对于每个aia_iai,枚举它的子集,然后如果子集中有在序列中位于其右边的数,那么就将答案统计上去。复杂度我也不是特别会证,反正复杂度就是O(跑得过)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int read(){
	char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
	while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
const int MD=1e9+7;
int n,a[220000],p[240000];
ll ans,sum[240000];
int calc(int x){
	if(sum[x]) return sum[x];
	int res=0;sum[x]=1;
	for(int i=x;i;i=(i-1)&x){
		if(i==x||!p[i]) continue;
		if(p[i]>p[x]) (res+=calc(i))%=MD;
	}
	(sum[x]+=res)%=MD;
	return sum[x];
}
int main()
{
	n=read();
	for(int i=1;i<=n;i++) a[i]=read(),p[a[i]]=i;
	for(int i=1;i<=n;i++) (ans+=calc(a[i]))%=MD;
	printf("%lld",(ans-n+MD)%MD);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值