トピックリンク:ポイント私はああ╭(╯^╰)╮
効果の件名:
の数、および任意の数を選択する、の合計
のプログラムの種類の
すべてのプログラムのために必要な番号を小数
数と
問題解決のアイデア:
、前処理の前と後の、二分探索することができます
のビットの全ての組み合わせは、
各シナリオの下小数でカウントされます
そして、それは一人ひとりの貢献列挙することによって見つけることができる
計算各1の寄与を、その位置の将来を検討する唯一の必要性、からなるプログラム
と必要性のみを検討
そして、
、
と
、それぞれ、二つのグループから
それぞれのニーズにソートするように、それぞれの寄与の計算は、前の番号を無視するので、
時間計算量を:
そこにこの1つは唯一で、ソートした後ので、正確に基数ソートの性質に合わせて、最適化を考えてみましょう
、各基数ソートを実行する時間を
各並び順
、
減少するだろう、二重のポインタでスキャンすることが可能である
合計時間の複雑さ:
コア:基数ソート、バイナリ検索+ +デュアルポインタ
#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
const int maxn = 2e6 + 5;
int T, n, n1, n2, w[maxn];
ll a[maxn], b[maxn], pw[15];
ll aa[maxn], bb[maxn];
vector <int> cnt[15];
void init() {
scanf("%d", &n);
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
for(int i=0; i<n; i++) scanf("%d", w+i);
int m = n / 2;
n1 = 1 << m, n2 = 1 << (n - m);
for(int i=0; i<n1; i++)
for(int j=0; j<20; j++)
if(i & (1ll << j)) a[i] += w[j];
for(int i=0; i<n2; i++)
for(int j=0; j<20; j++)
if(i & (1ll << j)) b[i] += w[j + m];
}
void ksort(int k) {
for(int i=0; i<=10; i++) cnt[i].clear();
for(int i=1; i<=n1; i++) cnt[(a[i]%pw[k])/pw[k-1]].push_back(a[i]);
for(int i=0, num=0; i<=10; i++) for(auto j : cnt[i]) a[++num] = j;
for(int i=0; i<=10; i++) cnt[i].clear();
for(int i=1; i<=n2; i++) cnt[(b[i]%pw[k])/pw[k-1]].push_back(b[i]);
for(int i=0, num=0; i<=10; i++) for(auto j : cnt[i]) b[++num] = j;
}
int main() {
scanf("%d", &T);
pw[0] = 1;
for(int i=1; i<11; i++) pw[i] = pw[i-1] * 10;
while(T--) {
init();
ll ans = 0;
for(int k=1; k<=10; k++) {
ksort(k);
for(int i=1; i<=n1; i++) aa[i] = a[i] % pw[k];
for(int i=1; i<=n2; i++) bb[i] = b[i] % pw[k];
for(int i=1, p1=n2, p2=n2; i<=n1; i++) {
while(aa[i] + bb[p1] >= 4ll * pw[k-1] && p1) p1--;
while(aa[i] + bb[p2] >= 5ll * pw[k-1] && p2) p2--;
ans += p2 - p1;
}
for(int i=1, p1=n2, p2=n2; i<=n1; i++) {
while(aa[i] + bb[p1] >= 14ll * pw[k-1] && p1) p1--;
while(aa[i] + bb[p2] >= 15ll * pw[k-1] && p2) p2--;
ans += p2 - p1;
}
}
printf("%lld\n", ans);
}
}