C_Dice Sum
题目
How many integer sequences of length
N
,
A
=
(
A
1
,
…
,
A
N
)
N, A=(A_1, \ldots, A_N)
N,A=(A1,…,AN), satisfy all of the conditions below?
1
≤
A
i
≤
M
(
1
≤
i
≤
N
)
1\le A_i \le M(1 \le i \le N)
1≤Ai≤M(1≤i≤N)
∑
i
=
1
N
A
i
≤
K
\displaystyle\sum _{i=1}^N A_i \leq K
i=1∑NAi≤K
Since the count can get enormous, find it modulo 998244353.
数据范围:
1
≤
N
,
M
≤
50
1≤N,M≤50
1≤N,M≤50
N
≤
K
≤
N
M
N \leq K \leq NM
N≤K≤NM
All values in input are integers.
题意:
给定三个数字n,m,k。计算出有多少个符合条件的序列。条件为序列中所有数字总和不超过K,并且序列包含n个数字,每个数字的值在1到m之间取值。不同位置视为不同的序列。
Sample input 1
2 3 4
Sample output 1
6
Sample input 2
31 41 592
Sample output 2
798416518
题解
本题目前有两种解法,一种是记忆化搜索,另一种为动态规划。记忆化搜索主要通过dfs遍历所有,随后优化剪枝。此处采用dp的方式进行求解。
(听说有大佬可以通过数学的方法推导公式,此处实力不足就不做说明了)
由题目分析,可以发现,当我知道前
i
−
1
i-1
i−1个数字之和为
j
j
j时的方案数,不难得出当我第
i
i
i个数字确定为
p
p
p,那么前
i
i
i个数字之和为
j
+
p
j+p
j+p的方案数中一定会包含前
i
−
1
i-1
i−1个数字之和为
j
j
j的方案。将
p
p
p从1到m进行枚举,那么得到的便是所有前
i
i
i个数字的组合情况。
d
p
[
i
]
[
j
]
=
∑
p
=
1
n
d
p
[
i
−
1
]
[
j
−
p
]
dp[i][j] = \sum_{p=1}^{n}dp[i-1][j-p]
dp[i][j]=∑p=1ndp[i−1][j−p]
dp[i][j]表示前i个数之和为j的方案数。
代码
#include<iostream>
using namespace std;
typedef long long ll;
const int mod = 998244353;
ll dp[60][3000];
int main() {
int n, m, k;
cin >> n >> m >> k;
dp[0][0] = 1;
for (int i = 1; i <= n; ++i) {//枚举n个数字
for (int j = 1; j <= k; ++j) {//枚举1到k的数字和
for (int p = 1; p <= m; ++p) {//枚举第i个数字的数值
if (j >= p) {//等于必须有,从0到1的转化
dp[i][j] += dp[i - 1][j - p];//转移方程
dp[i][j] %= mod;
}
}
}
}
ll ans = 0;
for (int i = k; i >= n; --i) {//因为是不超过k的方案数,因此最小到全为1的情况。
ans += dp[n][i];
ans %= mod;
}
cout << ans;
return 0;
}
D_Range Count Query
题目
You are given a sequence of length
N
:
A
=
(
A
1
,
…
,
A
N
)
N: A=(A_1,\ldots,A_N)
N:A=(A1,…,AN)
Answer
Q
Q
Q queries given in the following format.
You are given integers L, R, and X. Find the number of elements among
A
L
,
…
,
A
R
A_L, \ldots, A_R
AL,…,AR
whose values are equal to X.
Constraints
1
≤
N
≤
2
×
1
0
5
1 \leq N \leq 2\times 10^5
1≤N≤2×105
1
≤
A
i
≤
N
1 \leq A_i \leq N
1≤Ai≤N
1
≤
Q
≤
2
×
1
0
5
1 \leq Q \leq 2\times 10^5
1≤Q≤2×105
1
≤
L
≤
R
≤
N
,
1
≤
X
≤
N
,
1\le L \leq R \leq N, 1 \leq X \leq N,
1≤L≤R≤N,1≤X≤N, for each query.
All values in input are integers.
大意:
给定一个序列,然后有q次查询,每次查询给出左右端点位置,以及查询的数字x,对每次查询给出区间内x的次数。序列从1开始编号。
Sample input
5
3 1 4 1 5
4
1 5 1
2 4 3
1 5 2
1 3 3
Sample output
2
0
0
1
题解
本题有两种解法,第一种是开辟一个二维数组,对其中序列的每一个数字采用一行,一行中标记该数字的位置,从小到大逐个标记(建议采用vector向量)。随后在查询时,对左右端点在一行中进行二分查找,找出对应的位置,然后将两个下标相减即为区间内数字的个数。
第二种是采用分块的思想,对序列进行分块处理,记录每一块中各个数字的出现次数,然后再对每一块进行查询。(不建议这样写,因为数据范围若是再大一点便会失败)。最后的运行效率来看,解法一比解法二快了一倍多
代码
解法一:
//二分法
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> vt[200010];
int main() {
int n, q, x, l, r;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> x;
vt[x].push_back(i);
}
cin >> q;
int a, b;
while (q--) {
cin >> l >> r >> x;
a = lower_bound(vt[x].begin(), vt[x].end(), l) - vt[x].begin();
b = upper_bound(vt[x].begin(), vt[x].end(), r) - vt[x].begin();
cout << b - a << "\n";
}
return 0;
}
解法二:
//分块法
#include<iostream>
#include<cmath>
#include<unordered_map>
using namespace std;
int n, k;
int mp[500][200010];//不可用map哈希代替,否则会复杂度过大而超时
int a[200010];
int BL[200010];//存数据在哪一块中
void init() {
cin >> n;
k = (int)sqrt(1.0f * n);
int j = 1;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
mp[j][a[i]]++;//块中数据记录
BL[i] = j;
if (i % k == 0) ++j;
}
}
int query(int l, int r, int x) {
int ans = 0;
int L = BL[l] * k;
int R = (BL[r]-1) * k + 1;
if (BL[l]==BL[r]) {
for (int i = l; i <= r; ++i) {
if (a[i] == x) ++ans;
}
}
else {
for (int i = l; i <= L; ++i) {
if (a[i] == x) ++ans;
}
for (int i = BL[l] + 1; i <= BL[r] - 1; ++i) {
ans += mp[i][x];
}
for (int i = R; i <= r; ++i) {
if (a[i] == x) ++ans;
}
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
init();
int q, l, r, x;
cin >> q;
while (q--) {
cin >> l >> r >> x;
cout << query(l, r, x) << "\n";
}
return 0;
}
E_K-colinear Line
题目
You are given N points in the coordinate plane. For each
1
≤
i
≤
N
1\leq i\leq N
1≤i≤N, the
i
i
i-th point is at the coordinates
(
X
i
,
Y
i
)
(X_i, Y_i)
(Xi,Yi).
Find the number of lines in the plane that pass K or more of the N points.
If there are infinitely many such lines, print Infinity.
Constraints
1
≤
K
≤
N
≤
300
1 \leq K \leq N \leq 300
1≤K≤N≤300
∣
X
i
∣
,
∣
Y
i
∣
≤
1
0
9
\lvert X_i \rvert, \lvert Y_i \rvert \leq 10^9
∣Xi∣,∣Yi∣≤109
X
i
≠
X
j
o
r
Y
i
≠
Y
j
,
i
f
i
≠
j
.
X_i\neq X_jor Y_i\neq Y_j, if\quad i\neq j.
Xi=XjorYi=Yj,ifi=j.
All values in input are integers.
大意
给定n个点的二维坐标,再给出一个数字k,计算有至少k个点共线的数目。如果数字为无穷大,则输出Infinity。
Sample input 1
5 2
0 0
1 0
0 1
-1 0
0 -1
Sample output 1
6
Sample input 2
1 1
0 0
Sample output 2
Infinity
题解
本题采用暴力的方法进行求解,只需计算任意两点连线,判断其余点是否在该线上即可,当点数大于等于k时,线数加1.
两点确定一条直线,即只有在k为1的时候,数目为无穷。
代码
#include<iostream>
#include<vector>
using namespace std;
typedef long long ll;
int px[310], py[310];
bool f[310][310];//判断x,y所在直线是否已经探查过。
vector<int> vt;
bool judge(int a, int b,int c) {//采用向量叉乘的方法,判定三点是否共线
int p = (px[a] - px[b]) * (py[a] - py[c]);
int q = (px[a] - px[c]) * (py[a] - py[b]);
return p == q;
}
int main() {
int n, k, ans = 0;
cin >> n >> k;
if (k == 1) {
cout << "Infinity";
return 0;
}
for (int i = 1; i <= n; ++i) {
cin >> px[i] >> py[i];//为每个点编号
}
for (int i = 1; i < n; ++i) {//由任意两点确定直线
for (int j = i + 1; j <= n; ++j) {
if (f[i][j]) continue;
vt.clear();
vt.push_back(i);//统计该线上的点数与编号
vt.push_back(j);
for (int s = j + 1; s <= n; ++s) {
if (judge(i, j, s)) {
vt.push_back(s);
}
}
if (vt.size() >= k) ++ans;
for (int a = 0; a < vt.size() - 1; ++a) {//对共线上任意两点做出探查标记
for (int b = a + 1; b < vt.size(); ++b) {
f[vt[a]][vt[b]] = true;
}
}
}
}
cout << ans;
return 0;
}