「模拟赛20180307」三元组 exclaim 枚举+树状数组
題目描述
給定 \(n,k\) ,求有多少個三元組 \((a,b,c)\) 滿足 \(1≤a≤b≤c≤n\)且\(a + b^2 ≡ c^3\ (mod\ k)\)。
輸入
多組數據,第一行數據組數\(T\)。
每組數據兩個整數,\(n\)和\(k\)。
輸出
\(T\)行,每行一個整數,表示滿足條件的三元組的個數。
樣例
樣例輸入
1 10 7樣例輸出
27 //為什么老被和諧啊數據范圍
\(1≤n,k≤10^5\)
\(T≤400\)
時間限制\(4s\)
題解
與其他學校互測,然后做題感覺很不友好……
這道題數據很有特點(哪里很有特點了),\(10^5\)顯然是一個象征性的數字,它意味著\(O(n\ log\ n)\)是可以過的(這么大的\(T\)被無視了啊)。
那么很自然的想到,這個式子并沒有什么規律(我也很無奈啊),我們可以考慮枚舉\(a,b,c\)中的\(1\)個。
但是我們選擇哪一個比較好呢?容易想到,應該是\(c\),它的次數最高,不易計算。
接下來考慮一個簡化的問題,如果不取余\(k\),該怎么辦?
對于一個數\(b\),由于\(1≤a≤b\),顯然\(c^3\)只有在\([b^2+1,b^2+b]\)范圍內才有解,而且是唯一解。
所以每一個\(b\)可以為在\([b^2+1,b^2+b]\)的\(c^3\)提供一個解,這不就是區間增加一個值嗎?樹狀數組即可做到。
再考慮取余\(k\)時,發現情況如出一轍,一樣的做就可以了。唯一一個問題就是,\([b^2+1,b^2+b]\)可能長度超過了\(k\)。
這時能發現長度超過\(k\)后完全覆蓋了所有區域,任何一個\(c\)都可以使用這個\(b\),我們只需要一個計數器\(count\),每次增加\(\left \lfloor\frac{b}{k}\right \rfloor\)。
現在,這道題的解法就呼之欲出了。我們從小到大枚舉\(c\),先在樹狀數組\((tree)\)中\([c^2+1,c^2+c]\)的區間加上\(1\),并更新\(count\),答案就等于\(tree[c^3\%k]+count\)。時間復雜度為\(O(Tn\ log\ n)\)(再說一次請無視\(T\)的大小)
\(Code:\)
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define N 100005 #define ll long long int T, n, mod; ll ans, now, t[N]; void update(int x, int v) {for (int i = x; i <= mod; i += i & -i)t[i] += v; } ll getsum(int x) {ll ans = 0;for (int i = x; i; i -= i & -i)ans += t[i];return ans; } int main() {freopen("exclaim.in", "r", stdin);freopen("exclaim.out", "w", stdout);scanf("%d", &T);for (int cas = 1; cas <= T; cas++){scanf("%d%d", &n, &mod);ans = now = 0;memset(t, 0, sizeof(t));for (int i = 1; i <= n; i++){int l =(1ll * i * i + 1)% mod + 1, r =(1ll * i * i + i)% mod + 1;if (l <= r)update(l, 1), update(r + 1, -1);elseupdate(1, 1), update(r + 1, -1), update(l, 1);int c = 1ll * i * i % mod * i % mod;now +=(i - 1)/ mod;ans += getsum(c + 1) + now;}printf("Case %d: ", cas);cout << ans;putchar(10);} }最后的吐槽:\(exclaim\)并不是三元組的意思,是驚叫的意思……至于為什么,我也不知道……
轉載于:https://www.cnblogs.com/ModestStarlight/p/8528555.html
總結
以上是生活随笔為你收集整理的「模拟赛20180307」三元组 exclaim 枚举+树状数组的全部內容,希望文章能夠幫你解決所遇到的問題。