6516. 【GDOI2020模拟03.28】数二数(two)

题目

有一个\([1,n]\)的整数,可以询问\([L,R]\),表示整个整数是否在这个区间里。

计算有多少个询问集合,使得这些询问过后,无论整数是\([1,n]\)中哪一个都能被唯一确定。

\(n\leq 300\)

思考历程

简化一下题目大意:可以通过询问,将数字分成若干组。

一开始\([1,n]\)为一组。

某个询问\([L,R]\)之后,将这个东西与当前分成的若干组分别取交,从而进一步分组。

到最后如果每一组如果仅仅包含一个数字,那么就是一种合法的方案。

我只会强行状压DP……然而并没有这档部分分。


正解

正解是个玄妙的东西。官方题解在此。

分组的更加严谨的解释:如果两个数字被包含的询问集合完全相同,那么它们就被分到同一组。

给组别分配一个编号,令\(V_i\)表示数字\(i\)在哪个组。

如果\(V_i\)互不相同,那么这就是一个合法的方案;如果有至少一个相同,那么就不合法。

考虑用总方案减去不合法的方案。

有个奇妙的性质:如果存在四个数字\(a,b,c,d\)满足\(a<b<c<d\)且\(V_a=V_c,V_b=V_d\),那么一定有\(V_a=V_b=V_c=V_d\)

用人话说就是,每个组别的最左端和最右端形成的区间,不可能真相交

题解用一种奇妙的方法将\(V\)缩短。

假如\(V=[1,2,3,2,4,2,5,5,10,6,7,8,7,6,9]\)

可以通过以下方式构造出\(V‘\):

在\(V\)中从左往右扫,见到一个组别号\(x\),首先将\(x\)丢到\(V‘\)的后面,然后找出\(l_x\)和\(r_x\)分别表示类别为\(x\)的最左端和最右端,将\([l_x,r_x]\)从\(V\)中删除。

这个例子构造出来的\(V‘=[1,2,5,10,6,9]\)

至于这个东西有什么用,我先放出题解原文:

You might be wondering why is this helpful? Well, it‘s helpful because any set of questions that corresponds to \(V\) should respect the following:

  1. Some questions are contained by a removed subarray. There is basically no restriction here
  2. All the other questions should uniquely identify \(V‘\) - we have the first hint of dynamic programming

本人英语不好,解释得不好也请见谅……

要搞出\(V\)和\(V‘\)之间的联系,先考虑能得到\(V\)的询问集合。

  1. 部分的询问区间是已经被\(V\)变成\(V‘\)的过程中,删去的子区间所包含的。这些询问基本上对\(V‘\)没有限制。
  2. 除去那些对\(V‘\)没有用的询问,剩余询问集合应该唯一地确定\(V‘\)。

那么可以从\(V‘\)推到\(V\),限制只在新增的那些子区间中。

设\(f_i\)表示\([1,i]\)合法的方案数。考虑用总数减去不合法的。

\(g_{i,j}\)表示\(V\)总长为\(i\),经过缩减处理的\(V‘\)总长为\(j\)的方案数。这个东西就是\(V‘\)到\(V\)新增的限制。

\[f_i=2^{C_{i+1}^2}-\sum_{j=1}^{i-1}f_jg_{i,j}
\]

至于\(g_{i,j}\)的转移,考虑在\(V‘\)后面新增一个组别编号。分为两种情况:这个组别仅仅出现一次;这个组别出现了至少两次,枚举左端和右端之间空隙的长度\(k\),在这个长度为\(k\)的区间里的询问自由组合。

\[g_{i,j}=g_{i-1,j-1}+\sum_{k=0}^{}g_{i-k-2,j-1}2^{C_{k+1}^1}
\]

时间复杂度\(O(n^3)\)。

在网上查题解的时候还发现这个方法可以用拉格朗日反演来优化,时间复杂度\(O(n \lg n)\)的。

表示没有看懂,以后心血来潮就来这里看看。


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define N 310
int n,mo;
ll p2[N*N];
ll f[N],g[N][N];
int main(){
//	freopen("in.txt","r",stdin);
	freopen("two.in","r",stdin);
	freopen("two.out","w",stdout);
	scanf("%d%d",&n,&mo);
	p2[0]=1;
	for (int i=1;i<=(n+1)*n>>1;++i)
		p2[i]=p2[i-1]*2%mo;
	g[0][0]=1;
	for (int i=1;i<=n;++i)
		for (int j=1;j<=i;++j){
			ll s=g[i-1][j-1];
			for (int k=0;i-k-2>=0;++k)
				s=(s+g[i-k-2][j-1]*p2[(k+1)*k>>1])%mo;
			g[i][j]=s;
		}
	for (int i=1;i<=n;++i){
		ll s=0;
		for (int j=1;j<i;++j)
			s=(s+f[j]*g[i][j])%mo;
		f[i]=(p2[(i+1)*i>>1]-s%mo+mo)%mo;
	}
	printf("%lld\n",f[n]);
	return 0;
}

总结

好思维的一道题目……

如果在比赛的时候给我想,我死也想不出来……

论脑子的重要性……

原文地址:https://www.cnblogs.com/jz-597/p/12607909.html

时间: 2024-10-08 17:08:28

6516. 【GDOI2020模拟03.28】数二数(two)的相关文章

【GDOI2020模拟03.28】数二数(two) (计数动态规划):

? 先考虑如何判断一个询问集是否合法. 考虑询问一次\([l,r]\),能把\([1,l-1]∪[r+1,n]\)和\([l,r]\)区分开来. 现在定义一个块为一个没有被区分开极大的点集合. 当所有块的大小都是1的时候,这个方案就是合法. ? 性质: 1.一个块是由若干连续段组成,比如下面这样: 1112233111411 颜色为1的块由三段连续段. ? 2.块与块之间不会出现交叉的情况: 比如下面这样: 11122211122211 也就说两个块的关系,只有完全包含和互不相交两种. ? 设\

2019考研数学题源探析经典1000题习题+解析分册(数一+数二+数三)

资源链接:https://pan.baidu.com/s/1gnDlPrVEQG6bd003Un-5Kg2019考研数学题源探析经典1000题习题+解析分册(数一+数二+数三)考研数学刷题必备!如下: 原文地址:http://blog.51cto.com/14084127/2320170

6439. 【GDOI2020模拟01.17】小 ω 数排列

题目描述 Description Input Output Sample Input Sample Input1 4 10 3 6 2 9 Sample Input2 8 35 3 7 1 5 10 2 11 6 Sample Output Sample Output1 6 [样例 1 解释] 共有 6 个排列符合条件,它们是 (1, 3, 2, 4),(2, 4, 1, 3),(3, 1, 2, 4),(3, 1, 4, 2),(4, 2, 1, 3),(4, 2, 3, 1). Sample

【Nowcoder 上海五校赛】二数

题目描述: 我们把十进制下每一位都是偶数的数字叫做"二数". 小埃表示自己很聪明,最近他不仅能够从小数到大:2,3,4,5....,也学会了从大数到小:100,99,98...,他想知道从一个数开始数最少的数就得到一个二数.但是聪明的小森已经偷偷在心里算好了小埃会数到哪个二数,请你求出他要数到哪个数吧. 换句话说,给定一个十进制下最多105位的数字,请你求出和这个数字的差的绝对值最小的二数,若答案不唯一,输出最小的那个. 也就是说,给定数字n,求出m,使得abs(n-m)最小且m[i]

[转帖]从麒麟阁到凌烟阁,数一数各朝代开国功臣名将

从麒麟阁到凌烟阁,数一数各朝代开国功臣名将 http://www.sohu.com/a/232660923_717027 2018-05-23 21:33 中国5000年历史,朝代更替,很多人跟随君主打天下,图的是功成名就,拜将封侯.功臣们获得至高无上的荣誉,享受人间繁华,这是用热血换来的回报,也是应该得到的尊荣. 西汉初年,汉高祖刘邦将萧何.曹参.宣张敖.周勃.樊哙等18人封侯,首次封赏开国功臣. 公元前51年,西汉宣帝刘询因匈奴归降大汉,回忆往昔辅佐有功之臣,令人画名功臣图像于麒麟阁以示纪念

12月28日 二维数组的应用:第一个小游戏(推箱子)

小游戏:******推箱子******** static void Main(string[] args) { int i, j; int[,] a = new int[10, 10]                  //二维数组的定义           类型[,] 数组名 = new  类型 [行数, 列数] {赋值}:   或单个赋值 a[i,j]=1; { {1,1,1,1,1,1,1,1,1,1}, {1,0,0,0,0,0,0,0,0,1}, {1,0,2,0,0,8,0,0,0,

fzu Problem 2198 快来快来数一数 (快速幂+优化)

题目链接: Problem  2198  快来快来数一数 题目描述: 给出n个六边形排成一排,a[i]代表i个六边形能组成的生成树个数,设定s[i]等于a[1]+a[2]+a[3]+....+a[i-1]+a[i],问s[n]为多少? 解题思路: n取值范围[1, 1018],打表内存不够,然后就要考虑快速幂咯!纳尼!!!!快速幂写出来竟然超时,敢信?果然还是见题太少了.(GG) 对于a[n] = 6*a[n-1] - a[n-2],可以很明显看出. 然后求和的时候就要化简一番了,但是并不是很难

一群猴子排成一圈,按1,2,...,n依次编号。然后从第1只开始数,数到第m只,把它踢出圈

一 群猴子排成一圈,按1,2,…,n依次编号.然后从第1只开始数,数到第m只,把它踢出圈,从它后面再开始数,再数到第m只,在把它踢出去…,如此不停的 进行下去,直到最后只剩下一只猴子为止,那只猴子就叫做大王.要求编程模拟此过程,输入m.n, 输出最后那个大王的编号 <?php //$n猴子个数 $m第几个位置 function fn( $n, $m){ //将猴子数量放到数组内 for($i = 1; $i < $n+1; $i++){ $arr[] = $i; } $i = 0; var_d

OCJP(1Z0-851) 模拟题分析(二)

Exam : 1Z0-851 Java Standard Edition 6 Programmer Certified Professional Exam 以下分析全都是我自己分析或者参考网上的,定有疏漏,还请大家对我的分析提出质疑. QUESTION 31 Given:1. interface A { public void aMethod(); }2. interface B { public void bMethod(); }3. interface C extends A,B { pub