赞
踩
给定一个 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
×
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
<P
<P,所以个数一定
≥
2
≥2
≥2 且最大是
P
−
1
P-1
P−1,每次拿前
K
−
1
K-1
K−1大的数,剩下的一个数从小数中拿,由于剩下的数的和一定
≥
P
≥P
≥P,所以最后拿完
P
P
P次后,拿出来的数的和一定满足
P
×
K
>
s
u
m
P×K>sum
P×K>sum
3. 如果
n
u
m
=
K
−
2
num=K-2
num=K−2,那么为了保证不等式剩下的数的和一定
≥
P
≥P
≥P
又由于大小
<
P
<P
<P,所以个数一定
≥
3
≥3
≥3,且最大是
P
−
1
P-1
P−1,每次拿前
K
−
2
K-2
K−2大的数,剩下的一个数从小数中拿,第一次拿一定成功,拿完后,开始第二次拿,此时不等式就是
s
u
m
−
k
≥
P
∗
K
−
K
sum-k≥P*K-K
sum−k≥P∗K−K,如果第二次拿失败了,说明剩下的
3
3
3个数中,有至少2个数是
0
0
0,这就说明之前有两个数是
1
1
1,且这两个数是剩下的数中最大的两个,那么就说明剩下的三个数是最大情况就是 1,1,0,由于而这与中间推导的个数一定
>
=
3
>=3
>=3,所以第二次拿一定是可以的,…一直推导到可以拿
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
如果你有任何建议或者批评和补充,请留言指出,不胜感激
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。