一次考试的简单T3

 

 

 

 

 

 我的第一个想法其实是毫无头绪

根本就想不到dp,直接就写了爆搜
后来讲了才知道。。。

这种dp的状态好像是一类dp的模型,他们的状态都有这样的一维:以第i个数结尾。
这样的dp有什么样的标志呢?
以第i个数为结尾,说明这个状态和第i个数是有关系的,一般是选择数列中的数字
这种状态在于他的状态出来了,转移一般也能够直接显示出来,然后在转移中会枚举前面的状态进行转移,然后就可以在枚举中进行优化。
最后一般是O(n)或者O(n log n)的复杂度.

这里有一道这样的dp题目

http://acm.hdu.edu.cn/showproblem.php?pid=4055
这道题目呢,不要去考虑每一个数的大小,在我们的眼里,它应该是只能是一个大小的关系,至于到底是几,在每一个状态里面根本就没有考虑的必要。也没有意义
设f[i][j]为选到了第i个数,目前以j结尾的可能性。
转移就很明显了
这里就不讲了

这道题目和这个T3是一样的,状态中都有一维是以i结尾。
因为在目前,我们的决策只是取决于上一个数的大小,所以只用记录一下就行了

这道题目呢,我们发现这是异或。
异或有一个奇妙的性质,两个数之间如果他们差的绝对值越小,他们的异或值越小。
这个是我写上一个T3时发现的。。。

那么,我们先拍个序,就可以发现,如果要满足这个任意两个数的异或都要大于x,那就是要满足,任意一个数和其他的数异或的最小值都要大于x
上面已经讲了,一个数异或的最小值就是和它最近的数的异或,在排序后就是他左右的数
所以我们只需要保证目前这个阶段在转移的时候合法即可,这个阶段的决策完全不会影响到其他任何的决策.这其实是我应该想到的。

所以就有了O(n^2)暴力

//¼ÓÓÍ
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int f[100001],n,a[100001],x; 
inline ll read()
{
	char c=getchar();ll a=0,b=1;
	for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
	for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;
	return a*b;
}
int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	n=read();x=read();
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
	}
	sort(a+1,a+1+n);
	f[1]=0;
	for(int i=1;i<=n;i++)
	{
		
		f[i]++;
		for(int j=1;j<i;j++)
		{
			if((a[j]^a[i])>=x)
			{
				f[i]+=f[j];
			}
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++)ans+=f[i];
	cout<<ans<<endl;
	return 0;
}

 真不戳

我们发现这个转移中,每一次都要用O(n)去查找a[j]使a[i]^a[j]比x大。
我们需要一个东西来快速查找比x大的a[i]^a[j],这时我们想到了trie树.
我们把每一个f[j]都先放进trie数中,每次就是在询问a[i]^a[j]>x的f[j]之和,这个可以直接在trie树上维护子树和。

时间复杂度O(n log max(a[i]))

posted @ 2020-09-17 21:17  HL_ZZP  阅读(162)  评论(0编辑  收藏  举报