[題解] CF818E Card Game Again
阿新 • • 發佈:2021-06-17
前言
題意
給定 \(n\) 個數 \(a_1\) ~ \(a_n\) ,與 \(k\) 。問有多少個區間 \([l,r]\) 的積能被 \(k\) 整除。
兩個區間不同當且僅當 \(l\) 不同或 \(r\) 不同。
思路
可以列舉左端點,然後去找滿足條件的右端點。
首先將 \(k\) 分解質因數。
可以發現, \(a_i\) 分解質因數後,只有和有 \(k\) 相同的質因數才有用,否則對於答案是沒有貢獻的。那麼就可以將有用的質因數給用桶給統計起來。
再將這些桶進行字首和。可以在 \(O(\log n)\) 的時間複雜度內求出所有的對整除影響的質數有多少。
在這個區間內,若對於每個質數的個數均大於 \(k\) 分解後對應質數的個數,則代表這個區間可以整數 \(k\) 。
那麼只要列舉左區間,都可以二分出右區間。
因為一個數 \(b\) 最多隻有 \(\log b\) 個質因子,所以總的時間複雜度為 \(O(n\log n\log k+\sqrt k)\)
Code
#include <map> #include <cmath> #include <cstdio> #include <vector> using namespace std; namespace Quick_Function { template <typename Temp> void Read(Temp &x) { x = 0; char ch = getchar(); bool op = 0; while(ch < '0' || ch > '9') { if(ch == '-') op = 1; ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); } if(op) x = -x; } template <typename T, typename... Args> void Read(T &t, Args &... args) { Read(t); Read(args...); } template <typename Temp> Temp Max(Temp x, Temp y) { return x > y ? x : y; } template <typename Temp> Temp Min(Temp x, Temp y) { return x < y ? x : y; } template <typename Temp> Temp Abs(Temp x) { return x < 0 ? (-x) : x; } template <typename Temp> void Swap(Temp &x, Temp &y) { x ^= y ^= x ^= y; } } using namespace Quick_Function; #define int long long #define mkp(x, y) make_pair(x, y) const int MAXN = 1e5 + 5; vector<pair<int, int> > vec; int a[MAXN], n, k; int cnt[MAXN][50], ans; bool Check(int l, int r) { for(int i = 0; i < vec.size(); i++) if(cnt[r][i] - cnt[l - 1][i] < vec[i].second) return 0; return 1; } signed main() { Read(n, k); a[n + 1] = k; for(int i = 1; i <= n; i++) Read(a[i]); for(int i = 2; i <= sqrt(k); i++) { if(k % i == 0) vec.push_back(mkp(i, 0)); while(k % i == 0) { k /= i; vec[vec.size() - 1].second++; } } if(k != 1) vec.push_back(mkp(k, 1)); for(int i = 1; i <= n + 1; i++) { int tmp = a[i]; for(int j = 0; j < vec.size(); j++) { cnt[i][j] = cnt[i - 1][j]; while(tmp % vec[j].first == 0) { tmp /= vec[j].first; cnt[i][j]++; } } } for(int i = 1; i <= n; i++) { int l = i, r = n + 1; while(l < r) { int mid = (l + r) >> 1; if(Check(i, mid)) r = mid; else l = mid + 1; } ans += n - l + 1; } printf("%lld", ans); return 0; }