P5629 【AFOI-19】区间与除法 题解

题目链接:P5629 【AFOI-19】区间与除法 题解

线段树/ST表好题

这道题目需要观察题目性质。

机房考试的时候想出来了一个不用位运算的Tle做法,结果因为没开longlong直接见祖宗爆0了

思路:

不难发现,这道题的\(m\)给得很小,于是我们可以在给定的“原数”上下功夫。

对于一个“原数”,我们假设一个数变为“原数”后,假设这个“原数”是\(p\),继续除以\(d\)能变为另外一个“原数”\(q\),那我们第一次得到的那个“原数”\(p\)显然没有继续除以\(d\)得到的那个“原数”\(q\)"优",因为我们可以简单的知道,能够还原成“原数”\(p\)的数一定可以还原成“原数”\(q\)

我们这样消除后一些“原数”后,发现对于每一个数,要么它就不能被除成“原数”,要么就可以被除成唯一一个对应的“原数”,因为如果它可以得到两个及以上“原数”,根据上面提到的更小的“原数”更"优",那些大的“原数”都会被消除掉。

我们可以预处理出每一个数可以被消成哪一个“原数”,因为\(d\) >= 2,对于每一个数处理的时间复杂度是\(log\)\((数的大小)\)的,所以可以暴力预处理,时间复杂度为:O(\(nlog(n)\))

然后我们发现\(m\) <= 60 种,根据一开始的消除后会更小,于是想到状态压缩,但是我考试的时候没想那么多,就直接开一个bool数组,用线段树去维护区间出现的不同数的个数就行了,然而,这样子是会T的.......但是其实消除后的\(m\)可能并不是那么大,于是在luogu上吸口O2就过去了......题解里面都是ST表

没有状态压缩的线段树,开O2过去了(不开会被卡成70pts)

用时
7.59s(最慢的一个点是2.18s)

内存
85.33MB

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 500005;
long long n,m,d,p;
long long a[MAXN] ,c[65], b[65],ss[65],Ans[65];
int color[MAXN];

struct tree {
	bool book[61];
	int l,r;
} T[MAXN * 4];

void Get(int x,int l,int r)
{
	if(T[x].l >= l && T[x].r <= r)
	{
		for(int i = 1 ; i <= m ; i ++)Ans[i] |= T[x].book[i];
		return ;
	}
	int mid = ( T[x].l + T[x].r ) >> 1;
	if(l <= mid)Get(x << 1 , l , r);
	if(r  > mid)Get(x << 1 | 1 ,l , r );
	return ;
}

void build(int x,int l,int r)
{
	T[x].l = l , T[x].r = r ;
	if(l == r){T[x].book[color[l]] = 1;return ;}
	int mid = (l + r) >> 1;
	build(x << 1 , l , mid);
	build(x << 1 | 1 , mid + 1 , r);
	for(int i = 1 ; i <= m ; i ++)
		if(T[x << 1].book[i] || T[x << 1 | 1].book[i])
		T[x].book[i] = 1 ;
	return ;
}

inline long long read()
{
	long long x = 0, flag = 1;
	char ch = getchar();
	for( ; ch > '9' || ch < '0' ; ch = getchar());
	for( ; ch >= '0' && ch <= '9'; ch = getchar())x = (x << 3) + (x << 1) + ch - '0';
	return x * flag;
}

inline void write(int x)
{
	if(!x){putchar('0'),putchar('\n');return ;}
	char W[30];
	int R = 0;
	while(x)
		R ++ , W[R] = x % 10 + '0',x = x / 10;
	for(int i = R ; i >= 1 ; i --)
		putchar(W[i]);
	putchar('\n');
	return ;
}

signed main()
{
	//freopen("purify.in","r",stdin);
	//freopen("purify.out","w",stdout);
	n = read() , m = read() , d = read() , p = read();
	for(int i = 1 ; i <= n ; i ++)a[i] = read();
	for(int i = 1 ; i <= m ; i ++)b[i] = read(),c[i] = 1;
	sort(b + 1 , b + 1 + m);
	for(int i = m ; i >= 1 ; i --)
	{
		long long t = b[i];
		while(t && c[i] == 1)
		{
			for(int j = i - 1 ; j >= 1 ; j --)
			if(b[j] == t)c[i] = 0;
			t /= d;
		}
	}
	int R = 0;
	for(int i = 1 ; i <= m ; i ++)
		if(c[i])R ++, ss[R] = b[i];//将消除后得到的"原数"拷贝
	m = R;
	for(int i = 1 ; i <= m ; i ++)
		b[i] = ss[i];
	for(int i = 1 ; i <= n ; i ++)
	{
		long long t = a[i],flag = 0;
		while(t)
		{
			for(int j = 1 ; j <= m ; j ++)
			if(t == b[j]){flag = 1 ; color[i] = j ;break;}
			t /= d;
		}
	}
	build(1 , 1 , n);
	while(p)
	{
		long long l,r,ans = 0;
		l = read() , r = read();
		for(int i = 1 ; i <= m ; i ++)Ans[i] = 0;
		Get(1,l,r);
		for(int i = 1 ; i <= m ; i ++)
			if(Ans[i] == 1)ans ++;
		write(ans);
		p --;
	}
	return 0;
}

然而,后来我想到了用bitset来搞

用时
3.89s

内存
36.86MB

还是开了O2才过,内存以及时间上有了极大优化,不开被卡成80pts,因为据万神说,bitset有一个大常数

Code

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 500005;
long long n,m,d,p;
long long a[MAXN] ,c[65], b[65],ss[65];
bitset <61> Ans;
int color[MAXN];

struct tree {
	bitset <61> book;
	int l,r;
} T[MAXN * 4];

void Get(int x,int l,int r)
{
	if(T[x].l >= l && T[x].r <= r)
	{
		Ans |= T[x].book;
		return ;
	}
	int mid = ( T[x].l + T[x].r ) >> 1;
	if(l <= mid)Get(x << 1 , l , r);
	if(r  > mid)Get(x << 1 | 1 ,l , r );
	return ;
}

void build(int x,int l,int r)
{
	T[x].l = l , T[x].r = r ;
	if(l == r){T[x].book[color[l]] = 1;return ;}
	int mid = (l + r) >> 1;
	build(x << 1 , l , mid);
	build(x << 1 | 1 , mid + 1 , r);
	T[x].book = T[x << 1].book | T[x << 1 | 1].book;
	return ;
}

inline long long read()
{
	long long x = 0, flag = 1;
	char ch = getchar();
	for( ; ch > '9' || ch < '0' ; ch = getchar());
	for( ; ch >= '0' && ch <= '9'; ch = getchar())x = (x << 3) + (x << 1) + ch - '0';
	return x * flag;
}

inline void write(int x)
{
	if(!x){putchar('0'),putchar('\n');return ;}
	char W[30];
	int R = 0;
	while(x)
		R ++ , W[R] = x % 10 + '0',x = x / 10;
	for(int i = R ; i >= 1 ; i --)
		putchar(W[i]);
	putchar('\n');
	return ;
}

signed main()
{
	//freopen("purify.in","r",stdin);
	//freopen("purify.out","w",stdout);
	n = read() , m = read() , d = read() , p = read();
	for(int i = 1 ; i <= n ; i ++)a[i] = read();
	for(int i = 1 ; i <= m ; i ++)b[i] = read(),c[i] = 1;
	sort(b + 1 , b + 1 + m);
	for(int i = m ; i >= 1 ; i --)
	{
		long long t = b[i];
		while(t && c[i] == 1)
		{
			for(int j = i - 1 ; j >= 1 ; j --)
			if(b[j] == t)c[i] = 0;
			t /= d;
		}
	}
	int R = 0;
	for(int i = 1 ; i <= m ; i ++)
		if(c[i])R ++, ss[R] = b[i];
	m = R;
	for(int i = 1 ; i <= m ; i ++)
		b[i] = ss[i];
	for(int i = 1 ; i <= n ; i ++)
	{
		long long t = a[i],flag = 0;
		while(t)
		{
			for(int j = 1 ; j <= m ; j ++)
			if(t == b[j]){flag = 1 ; color[i] = j ;break;}
			t /= d;
		}
	}
	build(1 , 1 , n);
	while(p)
	{
		long long l,r,ans = 0;
		l = read() , r = read();
		for(int i = 1 ; i <= m ; i ++)Ans[i] = 0;
		Get(1,l,r);
		for(int i = 1 ; i <= m ; i ++)
			if(Ans[i] == 1)ans ++;
		write(ans);
		p --;
	}
	return 0;
}

最后放上不开O2也能过的状态压缩版

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 500005;
long long n,m,d,p;
long long a[MAXN] ,c[65], b[65],ss[65];
long long Ans;
int color[MAXN];

struct tree {
	long long book;
	int l,r;
} T[MAXN * 4];

void Get(int x,int l,int r)
{
	if(T[x].l >= l && T[x].r <= r)
	{
		Ans |= T[x].book;
		return ;
	}
	int mid = ( T[x].l + T[x].r ) >> 1;
	if(l <= mid)Get(x << 1 , l , r);
	if(r  > mid)Get(x << 1 | 1 ,l , r );
	return ;
}

void build(int x,int l,int r)
{
	T[x].l = l , T[x].r = r ;
	if(l == r){
		if(color[l])
		T[x].book |= (1ll << color[l]);
		return ;
	}
	int mid = (l + r) >> 1;
	build(x << 1 , l , mid);
	build(x << 1 | 1 , mid + 1 , r);
	T[x].book = T[x << 1].book | T[x << 1 | 1].book;
	return ;
}

inline long long read()
{
	long long x = 0, flag = 1;
	char ch = getchar();
	for( ; ch > '9' || ch < '0' ; ch = getchar());
	for( ; ch >= '0' && ch <= '9'; ch = getchar())x = (x << 3) + (x << 1) + ch - '0';
	return x * flag;
}

inline void write(int x)
{
	if(!x){putchar('0'),putchar('\n');return ;}
	char W[30];
	int R = 0;
	while(x)
		R ++ , W[R] = x % 10 + '0',x = x / 10;
	for(int i = R ; i >= 1 ; i --)
		putchar(W[i]);
	putchar('\n');
	return ;
}

signed main()
{
	//freopen("purify.in","r",stdin);
	//freopen("purify.out","w",stdout);
	n = read() , m = read() , d = read() , p = read();
	for(int i = 1 ; i <= n ; i ++)a[i] = read();
	for(int i = 1 ; i <= m ; i ++)b[i] = read(),c[i] = 1;
	sort(b + 1 , b + 1 + m);
	for(int i = m ; i >= 1 ; i --)
	{
		long long t = b[i];
		while(t && c[i] == 1)
		{
			for(int j = i - 1 ; j >= 1 ; j --)
			if(b[j] == t)c[i] = 0;
			t /= d;
		}
	}
	int R = 0;
	for(int i = 1 ; i <= m ; i ++)
		if(c[i])R ++, ss[R] = b[i];
	m = R;
	for(int i = 1 ; i <= m ; i ++)
		b[i] = ss[i];
	for(int i = 1 ; i <= n ; i ++)
	{
		long long t = a[i],flag = 0;
		while(t)
		{
			for(int j = 1 ; j <= m ; j ++)
			if(t == b[j]){flag = 1 ; color[i] = j ;break;}
			t /= d;
		}
	}
	build(1 , 1 , n);
	while(p)
	{
		long long l,r,ans = 0;
		l = read() , r = read();
		Ans = 0;
		Get(1,l,r);
		while(Ans)
		{
			ans++;
			Ans-=(Ans&-Ans);
		}
		write(ans);
		p --;
	}
	return 0;
}
posted @ 2020-11-03 17:06  MYCui  阅读(151)  评论(0编辑  收藏  举报