總體來說不算好做……但是確實也不難。
A
給定正整數n,k,求 ,結果對19260817取模。
1 <= n <= 1e7, 0 <= k <= 1e9
時限0.698s(???)
上來用拉格朗日插值法直接WA。。正解其實比想象的簡單。先歐拉篩出素數,對素數用快速冪求出i^k存起來。然後對合數的i^k只要用素數的結果求就可以了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int p = 19260817;
const int maxn = 1e7+5;
bool flag[maxn];
ll f[maxn], prime[maxn], n, k, cnt;
inline ll pow_mod(ll a, ll b)
{
ll res = 1;
while (b)
{
if (b & 1) res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res;
}
inline void sieve()
{
for (int i = 2; i <= n; ++i)
{
if (!flag[i])
{
prime[cnt++] = i;
f[i] = pow_mod(i, k);
}
for (int j = 0; j < cnt && i * prime[j] <= n; ++j)
{
flag[i * prime[j]] = 1;
f[i * prime[j]] = f[i] * f[prime[j]] % p;
if (i % prime[j] == 0) break;
}
}
}
int main()
{
scanf("%lld%lld", &n, &k);
ll sum = 1;
sieve();
for (ll i = 2; i <= n; ++i)
sum = (sum + f[i]) % p;
printf("%lld\n", sum);
return 0;
}
B
給出一個長度爲 N 的整數數列 A,對於這個數列進行任意多操作。每次選擇一個任意的整數,並將任意 P 個數字加上這個數字。輸出 YES 或 NO,表示能否通過這種方法將這個數列中每個數字同時變成零。
1 ≤ P ≤ N ≤ 1e5, |Ai| <= 1e6
結論題。顯然當n==p時只有所有數字相等時纔是YES;n>p時,數字總和如果是p的倍數則YES。其實憑感覺可以想到:要把所有數字變成0,和必須是p的倍數。不過嚴格的證明則是將所有n>p的情況轉化爲n==p+1的情況。
#include <bits/stdc++.h>
using namespace std;
int n, p, i, tmp, sum;
int main()
{
cin >> n >> p;
if (n == p)
{
scanf("%d", &tmp);
for (i = 1; i < n; ++i)
{
scanf("%d", &sum);
if (sum != tmp) break;
}
printf("%s\n", i == n ? "YES" : "NO");
}else
{
for (i = 0; i < n; ++i)
{
scanf("%d", &tmp);
sum += tmp;
}
printf("%s\n", sum % p ? "NO" : "YES");
}
return 0;
}
C
給任意一個大於 1 的正整數 N,輸出 N 可以分解成最少幾個質數(可以相同)的和。
2 ≤ N ≤ 1e15
據說是Codeforces原題。
如果本身是質數那麼直接輸出1。
運用哥德巴赫猜想,任何大於2的偶數可以被分解爲兩個質數之和。也就是偶數輸出2。
如果n是奇合數但還可以被分解爲兩個質數,那隻可能是一奇一偶。偶質數只能是2,也就是說n-2必須是質數。
其他情況輸出3。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n;
inline bool isprime(ll m)
{
for (ll i = 2; i * i <= m; ++i)
if (!(m % i)) return 0;
return 1;
}
int main()
{
cin >> n;
if (n == 2) printf("1\n");
else if (!(n&1)) printf("2\n");
else if (isprime(n)) printf("1\n");
else if (isprime(n-2)) printf("2\n");
else printf("3\n");
return 0;
}
D
給出 n 個正整數,問有多少種方法在這 n 個數字的中取其中一些數字,使得這些數字之和超過 k。若答案超過 20 000 000,輸出 -1。
1 ≤ n ≤ 1e4
1 ≤ ai ≤ 1e8
1 ≤ k ≤ 1e10
降序排序,預處理前綴和,然後dfs+剪枝。
最優性剪枝:如果當前和已經大於k則剪枝,ans要加上2^(剩餘數字個數)——這一步還可以剪枝:由於2^25>2e7,所以一旦剩餘數字個數大於等於25也剪枝。
可行性剪枝:如果當前和加上後面所有數(用前綴和)都不大於k那麼剪枝。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e4+5;
int a[maxn], n, ans;
bool flag;
ll k, suma[maxn];
inline void dfs(int dep, ll sum)
{
if (flag || sum + suma[n]-suma[dep-1] <= k) return;
if (sum > k)
{
n-dep+1 >= 25 ? flag = 1 : ans += 1<<(n-dep+1);
return;
}
dfs(dep+1, sum + a[dep]);
dfs(dep+1, sum);
}
int main()
{
cin >> n >> k;
for (int i = 1; i <= n; ++i)
scanf("%d", a+i);
sort(a+1, a+1+n, greater<int>());
for (int i = 1; i <= n; ++i)
suma[i] = suma[i-1] + a[i];
dfs(1, 0);
printf("%d\n", flag ? -1 : ans);
return 0;
}
E
給出整數數列 {an},對整個數列進行儘可能少的次數操作,每次操作可以將數列中任意一項加 1 或者減 1,使得最終的數列 b1,b2,b3,…,bn 滿足對數列中的任一項 bi (i>=2),有 。
求最少的操作次數。1 ≤ n ≤ 1e5,1 ≤ ai ≤ 1e10
設{cn}={a1,a2-2,a3-5,a4-9…},這題等價於求 的最小值。根據高中函數知識,x應該取cn的中位數。那麼我們構造出cn然後排下序取中間下標,這題就做完了……
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
ll a[maxn], b[maxn], ans;
int n;
inline ll fun(ll mid)
{
ll ret = 0;
for (int i = 1; i <= n; ++i)
ret += abs(mid - a[i]);
return ret;
}
int main()
{
cin >> n;
b[2] = 2;
for (int i = 3; i <= n; ++i)
b[i] = b[i-1] + i;
for (int i = 1; i <= n; ++i)
{
scanf("%lld", a+i);
a[i] -= b[i];
}
sort(a+1, a+1+n);
printf("%lld\n", fun(a[(n+1)>>1]));
return 0;
}
F
對於給定的數字串 ,每次可以進行如下操作: 選擇一個數 i (1 < i < n),將 變成 。問在經過任意多次的操作後,該數列的數字總和最小爲多少?
1 ≤ n ≤ 1e5,0 ≤ ai ≤ 1e10
令 ,這樣c數列有n-1項。注意到對 的操作等價於交換 與 ,那麼通過將c升序排序後反構造出的a數列就是總和最小的數列啦。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
ll a[maxn], c[maxn], sum;
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; ++i)
scanf("%lld", a+i);
for (int i = 0; i < n-1; ++i)
c[i] = a[i+1] - a[i];
sort(c, c + n-1);
sum = a[0];
for (int i = 1; i < n; ++i)
{
a[i] = a[i-1] + c[i-1];
sum += a[i];
}
printf("%lld\n", sum);
return 0;
}
到今天爲止,終於把以前寫的需要多一些深入思考的題目記錄完畢了。