Part 1:前言
题目翻译
在他最喜欢的咖啡馆里,Kmes再次想尝尝皮草大衣下的鲱鱼。以前,这对他来说并不难,但咖啡馆最近推出了一项新的购买政策。
现在,为了进行购买,Kmes需要解决以下问题:在他面前摆放着
n
n
n 张不同价格的卡,第
i
i
i 张卡的价格为
a
i
a_i
ai,在这些价格中没有整数
x
x
x
。
Kmes被要求将这些卡划分为最少数量的坏段(这样每张卡只属于一个段)。如果无法选择乘积等于 x x x 的卡子集,则认为该段是坏的。Kmes 划分卡片的所有段都必须是坏的。
从形式上讲,如果没有下标 i 1 < i 2 < … < i k i_1<i_2<\ldots<i_k i1<i2<…<ik,使得 l ≤ i 1 , i k ≤ r l\le i_1,i_k\le r l≤i1,ik≤r 且 ∏ j = 1 k a i j = x \prod _ {j=1} ^ {k} a_{i_j} = x ∏j=1kaij=x,则段 ( l , r ) (l,r) (l,r) 是坏的。
帮助Kmes确定坏段的最小数量,以便享受他最喜欢的菜肴。
输入格式
第一行包含一个整数 t ( 1 ≤ t ≤ 1 0 3 ) t(1\le t\le 10^3) t(1≤t≤103)——测试用例的数量。
每组输入数据的第一行分别给出整数 n n n 和 x ( 1 ≤ n ≤ 1 0 5 , 2 ≤ x ≤ 1 0 5 ) x(1\le n\le 10^5,2\le x\le 10^ 5) x(1≤n≤105,2≤x≤105)——卡片数量和整数。
每组输入数据的第二行包含 n n n 个整数 a i ( 1 ≤ a i ≤ 2 ⋅ 1 0 5 , a i ≠ x ) a_i(1\le a_i\le 2\cdot 10^5,a_i\neq x) ai(1≤ai≤2⋅105,ai=x)——卡片上的价格。
保证所有测试数据集的 n n n 总和不超过 1 0 5 10^5 105。
输出格式
对于每组输入数据,输出坏段的最小数量。
Part 2:思路
这道题,咱就选择贪心吧(主要是我 dp 不大会)。
坏段 B B B 的定义: ∃ \exist ∃ 子序列 A ∈ B , ∏ A = x A \in B, \prod A=x A∈B,∏A=x。
显然,一个坏段的长度是有单调性的:当 a 1 , a 2 , … , a k a_1,a_2,\dots,a_k a1,a2,…,ak 是坏段时, a 1 , a 2 , … , a k + 1 a_1,a_2,\dots,a_{k+1} a1,a2,…,ak+1 也必定是坏段。
所以,我们每次判断出一个段是坏段时,就把 ans 加一。
怎么判断呢?考虑用一个速度更快的 unordered_set,存储之前所有子序列的乘积。
每次循环遍历这个 unordered_set,令当前遍历到的为 k k k。当 a i × k = x a_i \times k=x ai×k=x 时,我们就知道,已经有一个坏段产生了。我们把 unordered_set 清空,将 ans 加一。
但是这样时间复杂度太高,极端情况下,复杂度为 O ( n x ) ≈ 1 0 10 O(nx) \approx 10^{10} O(nx)≈1010,即使 4s 时限,也救不了你。
所以我们要考虑优化。
-
当 x m o d ( a i × k ) = 0 x\mod(a_i \times k)=0 xmod(ai×k)=0 时,将 a i × k a_i \times k ai×k 放入 unordered_set 里。
-
当 x m o d ( a i × k ) ≠ 0 x\mod(a_i \times k)\neq0 xmod(ai×k)=0 时,由于无论之后的数怎么样,子序列的乘积已经 ≠ x \neq x =x 了,就舍去。
Part 3:代码
你们终于来到了这里
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
ll t,n,x,a[N],ans;
unordered_set<ll> st,us;
inline void FIO(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
}
inline void work(){
st.clear();us.clear();ans=1;
cin>>n>>x;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
for(auto j:st){
ll k=a[i]*j;
if(x%k==0){
if(k==x){
ans++;st.clear();us.clear();
break;
}
us.insert(k);
}
}
for(auto j:us) st.insert(j);
us.clear();
if(x%a[i]==0) st.insert(a[i]);
}
cout<<ans<<endl;
}
signed main(){
FIO();
cin>>t;
while(t--) work();
}
满江绿的感受,是那样的美好,那么的舒适!