A. 數位和(digit)
題意:
設 \(f(x)\) 為 \(x\) 的數字和。例如 \(f(158)=1+5+8=14\)。
給定一個長度為 \(N\) 的正整數序列 \(A\),求 \(\sum_{i=1}^{N}\sum_{j=1}^{N}f(A_i+A_j)\)。
分析:
首先明確 \(f(x)\) 為 \(x\) 的數位和。
舉例情況:
若有兩個數分別為:\(12,21\)。
可以發現兩個數相加的數位和可以轉化為第一個數的數位和和第二個數數位和相加。
但總有特殊情況如:
這兩個數分別為:\(53,27\)。
可以發現這兩個數的數位相加時在個位上進了一位,就不符合上面的情況了,那該怎麼辦呢?
仔細思考進位對數位和的貢獻是什麼?
相當於此位變為 \(0\) 和更高的一位 \(+1\),則是對總答案相當於貢獻了 \(-9\)。
那麼我們就明確了計算的過程:
設 \(g(a,b)\) 表示 \(a+b\) 進位個數。
第二個式子為什麼是 \(2n\) 呢?因為它作 \(a_i\) 時加了 \(n\) 次,它作 \(a_j\) 時被加了 \(n\) 次。
那怎麼求 \(g(a,b)\) 呢?
對於兩個數 \(a,b\),如果 \(a+b\) 在第 \(x\) 位上發生了進位,那麼有 \(a+b\ge10^x\)。
那麼我們就可以列舉 \(x\),按照每個前 \(x\) 位的數的大小排序,再列舉 \(j\),二分找到第一個 \(a_j+a_k\ge10^i\) 的數的下標 \(k\),\(k\) 後面的數就全是可以進位的,然後就可以 \((n-k+1)\) 求出 \(g(a,b)\) 的個數了。
時間複雜度為 \(O(n\log{n})\)。
下面見程式碼(有註釋不要擔心):
注:十年OI一場空,不開long long 見祖宗
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int n;
ll x;
ll a[20][200005];//a[前幾位的數][第幾個數]
ll ans=0;
ll p(ll x){//計算數位和
ll sum=0;
while(x){
sum+=x%10;
x/=10;
}
return sum;
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&x);
ans+=2*n*p(x);//先不考慮進位,單純地都加上
ll mod=10;
for(int y=1;y<=15;y++){
a[y][i]=x%mod;//前i的數為記錄起來
mod*=10;
}
}
ll w=1;
for(int i=1;i<=15;i++){//列舉前i位
w*=10;
sort(a[i]+1,a[i]+1+n);//對前i位的數的大小進行排序
for(int j=1;j<=n;j++){//對每個數進行排序
//統計加上a[i][j]後大於等於10^i的個數
ll k=n+1-(lower_bound(a[i]+1,a[i]+1+n,w-a[i][j])-a[i]);
ans-=9*k;//減去進位減少的貢獻
}
}
cout<<ans;
return 0;
}
B. T2--集合變換(trans)
暴力搜尋,但要剪枝,考慮如果我們搜到了 \(1\) 就沒有必要向下遞迴了,這樣就成了沒有隻有一個兒子的節點,能省下很大時間,如果 \(k/ge m\) 說明我們 \(m\) 耗盡也只能搜到 \(1\),直接剪掉。
我們對初始值分解質因數,我的因數的因數也只能是我們因數,所以我們只要分解一次即可。
#include <bits/stdc++.h>
#define int long long
#define ls p<<1
#define rs p<<1|1
#define re register
const int N=5e5+10;
const int mod=1e9+7;
using namespace std;
int x,k,m;
int d[N],top;
int cnt;
int ans;
void dfs(int z,int x){
if(z==k||x==1){
cnt++;
ans+=x;
if(cnt>=m){
exit(0);
}
return;
}
for(int i=1;i<=top&&d[i]<=x;i++){
if(x%d[i]==0){
dfs(z+1,d[i]);
}
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>x>>k>>m;
if(k>=m){
cout<<m;
return 0;
}
for(int i=1;i*i<=x;i++){
if(x%i==0){
d[++top]=i;
if(i*i!=x){
d[++top]=x/i;
}
}
}
sort(d+1,d+top+1);
dfs(0,x);
cout<<ans;
return 0;
}