给定一个 N N N,现在问有多少个三元组 ( A , B , C ) (A,B,C) (A,B,C), A ≤ B ≤ C A≤B≤C A≤B≤C , A B C ≤ N ABC≤N ABC≤N
直接暴力枚举,时间复杂度 O ( N 2 3 ) O(N^{\frac{2}{3}}) O(N32),枚举A和B然后算出有多少个C
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int s[N];
#define ll long long
int main() {
ll n;
cin >> n;
ll res = 0;
for (ll i = 1; i * i * i <= n; i++) {
for (ll j = i; i * j * j <= n; j++) {
res += n / i / j - j + 1;
}
}
cout << res << endl;
}
给一个长度为 N N N的数列, A 1 . . . . A N A_1....A_N A1....AN, A i ≥ 1 A_i≥1 Ai≥1,现在每次选 K K K个数减去 1 1 1,每个数只能被减到 0 0 0,问整个数列最多能操作几次
假设可以操作 P P P次,现在令 s u m = sum= sum= ∑ i = 1 n m i n ( P , A i ) \sum_{i=1}^{n} min(P,A_i)\qquad ∑i=1nmin(P,Ai), <P <P
如果 P × K > s u m P×K>sum P×K>sum,那么很明显是不够减的,所以一定不成立
可以假设当 P × K ≤ s u m P×K≤sum P×K≤sum时,我们可以操作 P P P次
每次操作的策略始终都是拿最大的 K K K个数
假设 ≥ P ≥P ≥P的个数为 n u m num num
1. 如果 n u m ≥ K num≥K num≥K,那么此时一定是满足 P × K > s u m P×K>sum P×K>sum
2. 如果 n u m = K − 1 num=K-1 num=K−1,那么为了保证不等式剩下的数的和一定 ≥ P ≥P ≥P,又由于大小 < P
3. 如果 n u m = K − 2 num=K-2 num=K−2,那么为了保证不等式剩下的数的和一定 ≥ P ≥P ≥P
又由于大小 < P
…
K-1. 如果 n u m = 0 num=0 num=0,…个数一定 ≥ K − 1 ≥K-1 ≥K−1,…按照上面归纳总结即可
最后按照不等式 P × K ≤ s u m P×K≤sum P×K≤sum,来二分即可
洛谷也有别的结论及证明 link.
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
#define ll long long
ll a[N];
int n, k;
bool check(ll x) {
ll sum = 0;
ll cnt = 0;
for (int i = 1; i <= n; i++) {
sum += min(x, a[i]);
}
return sum >= x * k;
}
int main() {
cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
ll l = 0, r = 1e18 / k;
while (l < r) {
ll mid = (l + r + 1) / 2;
if (check(mid)) {
l = mid;
} else {
r = mid - 1;
}
}
cout << l << endl;
}
给定一个只有 K , E , Y K,E,Y K,E,Y字母的一个字符串,现在问最多能交换相邻字符串 K K K次,问能形成多少种字符串
原字符串定义为 A A A,变换后的字符串定义为 B B B
定义 d p ( i , e , y , x ) dp(i,e,y,x) dp(i,e,y,x)为 B B B字符串前 i i i位有 e 个 E e个E e个E y 个 Y y个Y y个Y 需要操作 x x x次才能变成与 A A A字符串前 i − e − y 个 K i-e-y个K i−e−y个K e 个 E e个E e个E y 个 Y y个Y y个Y的相对顺序一致
最终就是 d p ( l e n , e c n t , y c n t , i ) dp(len,ecnt,ycnt,i) dp(len,ecnt,ycnt,i)从0到k的累加
那么此时已知 d p ( i , e , y , x ) dp(i,e,y,x) dp(i,e,y,x)的话,看对 d p ( i + 1 ) dp(i+1) dp(i+1)产生什么影响即可
在当前状态下,如果末尾来了一个 K K K,这是第 i − e − y i-e-y i−e−y个 K K K为了保证相对位置一样,就需要新来的 K K K与原序列的第 i − e − y i-e-y i−e−y个 K K K连线,且保证与其他的线条不相交,此时那样的话就需要把当前这个 K K K往左移动几位,需要的步数,就是操作数,需要的步数就是,把 K K K到对应位置,需要跨越的 E 和 Y E和Y E和Y的个数,假设是 s s s
那么此时 d p ( i + 1 , e , y , x + s ) + = d p ( i , e , y , x ) dp(i+1,e,y,x+s)+=dp(i,e,y,x) dp(i+1,e,y,x+s)+=dp(i,e,y,x)
同理讨论 Y Y Y和 E E E即可
d p ( i + 1 , e + 1 , y , x + s ) + = d p ( i , e , y , x ) dp(i+1,e+1,y,x+s)+=dp(i,e,y,x) dp(i+1,e+1,y,x+s)+=dp(i,e,y,x)
d p ( i + 1 , e , y + 1 , x + s ) + = d p ( i , e , y , x ) dp(i+1,e,y+1,x+s)+=dp(i,e,y,x) dp(i+1,e,y+1,x+s)+=dp(i,e,y,x)
#include <bits/stdc++.h>
using namespace std;
const int N = 33, M = N * N;
#define ll long long
string s;
int k;
ll dp[N][N][N][M];
int main() {
cin >> s >> k;
int len = s.size();
k = min(k, len * (len - 1) / 2);
vector<int> kpos, epos, ypos;
for (int i = 0; i < len; i++) {
if (s[i] == 'K') {
kpos.push_back(i);
}
if (s[i] == 'E') {
epos.push_back(i);
}
if (s[i] == 'Y') {
ypos.push_back(i);
}
}
int kcnt = kpos.size(), ecnt = epos.size(), ycnt = ypos.size();
dp[0][0][0][0] = 1;
for (int i = 0; i < len; i++) {
for (int e = 0; e <= ecnt; e++) {
for (int y = 0; y <= ycnt; y++) {
int kk = i - e - y;
//非法情况
if (kk < 0 || kk > kcnt) continue;
for (int sw = 0; sw <= k; sw++) {
//无贡献,直接跳过,省时间
if (!dp[i][e][y][sw]) continue;
if (kk < kcnt) {
int step = 0;
for (int now = 0; now < e; now++) {
if (epos[now] > kpos[kk]) {
step++;
}
}
for (int now = 0; now < y; now++) {
if (ypos[now] > kpos[kk]) {
step++;
}
}
if (sw + step <= k) {
dp[i + 1][e][y][sw + step] += dp[i][e][y][sw];
}
}
if (e < ecnt) {
int step = 0;
for (int now = 0; now < kk; now++) {
if (kpos[now] > epos[e]) {
step++;
}
}
for (int now = 0; now < y; now++) {
if (ypos[now] > epos[e]) {
step++;
}
}
if (sw + step <= k) {
dp[i + 1][e + 1][y][sw + step] += dp[i][e][y][sw];
}
}
if (y < ycnt) {
int step = 0;
for (int now = 0; now < kk; now++) {
if (kpos[now] > ypos[y]) {
step++;
}
}
for (int now = 0; now < e; now++) {
if (epos[now] > ypos[y]) {
step++;
}
}
dp[i + 1][e][y + 1][sw + step] += dp[i][e][y][sw];
}
}
}
}
}
ll res = 0;
for (int i = 0; i <= k; i++) {
res += dp[len][ecnt][ycnt][i];
}
cout << res << endl;
}
n × m n×m n×m的矩阵,从 ( 1 , 1 ) (1,1) (1,1)点走到 ( n , m ) (n,m) (n,m)点,只可以往下走或者往右走,从起点走到终点的路径中,令 s u m = sum= sum=走的路径中 n + m − 1 n+m-1 n+m−1个数中,最大的 K K K个数的和,问这个和最小是多少
由于 n 和 m n和m n和m很小, n × m n×m n×m最大是900,所以第 K K K大的数字,最多有 900 900 900种情况,所以可以枚举第 K K K大的数字为 n o w now now,然后跑 d p dp dp
d p ( i , j , k ) dp(i,j,k) dp(i,j,k)为从起点到 ( j , k ) (j,k) (j,k),最大的 i i i个数的和
如果下一个点可达,并且下一个点的权值满足 ≥ n o w ≥now ≥now,那就是
d p ( i + 1 , j + 1 , k ) = m i n ( d p ( i + 1 , j + 1 , k ) , d p ( i , j , k ) + a ( j + 1 , k ) ) dp(i+1,j+1,k)=min(dp(i+1,j+1,k),dp(i,j,k)+a(j+1,k)) dp(i+1,j+1,k)=min(dp(i+1,j+1,k),dp(i,j,k)+a(j+1,k))
d p ( i + 1 , j , k + 1 ) = m i n ( d p ( i + 1 , j , k + 1 ) , d p ( i , j , k ) + a ( j , k + 1 ) ) dp(i+1,j,k+1)=min(dp(i+1,j,k+1),dp(i,j,k)+a(j,k+1)) dp(i+1,j,k+1)=min(dp(i+1,j,k+1),dp(i,j,k)+a(j,k+1))
如果下一个点的权值 ≤ n o w ≤now ≤now
那么就是
d p ( i , j + 1 , k ) = m i n ( d p ( i , j + 1 , k ) , d p ( i , j , k ) ) dp(i,j+1,k)=min(dp(i,j+1,k),dp(i,j,k)) dp(i,j+1,k)=min(dp(i,j+1,k),dp(i,j,k))
d p ( i , j , k + 1 ) = m i n ( d p ( i , j , k + 1 ) , d p ( i , j , k ) ) dp(i,j,k+1)=min(dp(i,j,k+1),dp(i,j,k)) dp(i,j,k+1)=min(dp(i,j,k+1),dp(i,j,k))
最后取 d p ( k , n , m ) dp(k,n,m) dp(k,n,m)即可
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll inf = 1e18;
const int N = 33;
int n, m, k;
int g[N][N];
ll dp[N][N][N + N];
int main() {
cin >> n >> m >> k;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> g[i][j];
}
}
ll res = inf;
for (int x = 1; x <= n; x++) {
for (int y = 1; y <= m; y++) {
int now = g[x][y];
memset(dp, 0x3f, sizeof(dp));
if (g[1][1] >= now) {
dp[1][1][1] = g[1][1];
}
if (g[1][1] <= now) {
dp[0][1][1] = 0;
}
for (int i = 0; i <= k; i++) {
for (int j = 1; j <= n; j++) {
for (int t = 1; t <= m; t++) {
if (j != n) {
if (i != k && g[j + 1][t] >= now) {
dp[i + 1][j + 1][t] =
min(dp[i + 1][j + 1][t],
dp[i][j][k] + g[j + 1][t]);
}
if (g[j + 1][t] <= now) {
dp[i][j + 1][t] =
min(dp[i][j + 1][t], dp[i][j][t]);
}
}
if (t != m) {
if (i != k && g[j][t + 1] >= now) {
dp[i + 1][j][t + 1] =
min(dp[i + 1][j][t + 1],
dp[i][j][t] + g[j][t + 1]);
}
if (g[j][t + 1] <= now) {
dp[i][j][t + 1] =
min(dp[i][j][t + 1], dp[i][j][t]);
}
}
}
}
}
res = min(res, dp[k][n][m]);
}
}
cout << res << endl;
}
让你求 C N K C_{N}^{K} CNK的因子个数, 0 ≤ N ≤ 1 0 12 , 0 ≤ K ≤ m i n ( 1 0 6 , N ) 0≤N≤10^{12},0≤K≤min(10^6,N) 0≤N≤1012,0≤K≤min(106,N)
由于数据范围很大,所有不可能把数算出来再进行质因数分解。把组合数展开, C N K = N ( N − 1 ) . . . . . . ( N − K + 1 ) 1 ⋅ 2 ⋅ 3...... K C_{N}^{K}=\frac{N(N-1)......(N-K+1)}{1·2·3......K} CNK=1⋅2⋅3......KN(N−1)......(N−K+1)
对分子和分母同时进行质因数分解,由于 N 和 K N和K N和K都很大,所以不能直接拿出分子分母的某个数进行质因数分解,可以发现 1 0 6 10^6 106以内的数,非常容易筛出素数,且分母的每个数在质因数分解后,最大的质因子一定在 1 0 6 10^6 106之内,而分子最大是 1 0 12 10^{12} 1012,质因数分解后,拥有的大于 1 0 6 10^6 106的素数一定只有 1 1 1个,且指数是 1 1 1,否则就超过 1 0 12 10^{12} 1012了。所以先筛出 1 0 6 10^6 106以内的素数,然后枚举每个素数,然后再枚举能整除对应素数的分子分母,然后算出其指数,最后筛完,看分子那些大数,是否有 1 0 6 10^6 106以上的素数即可。然后就是求约数之和的步骤了,时间复杂度大约是 O ( M l o g l o g M ) O(MloglogM) O(MloglogM)类似于埃氏筛, M = m i n ( N , K ) M=min(\sqrt{N},K) M=min(N,K)
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e6 + 10, mod = 998244353;
bool st[N];
int primes[N], cnt;
ll n, k;
void get_primes(int n) {
for (int i = 2; i <= n; i++) {
if (!st[i]) primes[cnt++] = i;
for (int j = 0; primes[j] <= n / i; j++) {
st[primes[j] * i] = 1;
if (i % primes[j] == 0) {
break;
}
}
}
}
int main() {
get_primes(N - 1);
cin >> n >> k;
vector<ll> deno(k + 1);
for (ll i = 1; i <= k; i++) {
deno[i] = i;
}
vector<ll> nums(k);
ll now = n - k + 1;
for (ll i = 0; i < k; i++) {
nums[i] = now + i;
}
ll res = 1;
for (int p = 2; p < N; p++) {
if (st[p] == 0) {
ll dex = 0;
for (ll i = p; i <= k; i += p) {
while (deno[i] % p == 0) {
dex--;
deno[i] /= p;
}
}
for (ll i = (n - k + 1 + p - 1) / p * p; i <= n; i += p) {
while (nums[i - now] % p == 0) {
dex++;
nums[i - now] /= p;
}
}
res = res * (dex + 1) % mod;
}
}
for (ll i = n - k + 1; i <= n; i++) {
if (nums[i - now] != 1) {
res = (ll)res * 2 % mod;
}
}
cout << res << endl;
}
To be continued
如果你有任何建议或者批评和补充,请留言指出,不胜感激