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;
}