AtCoder 248_CDE

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) 1AiM(1iN)
∑ i = 1 N A i ≤ K \displaystyle\sum _{i=1}^N A_i \leq K i=1NAiK
Since the count can get enormous, find it modulo 998244353.
数据范围:
1 ≤ N , M ≤ 50 1≤N,M≤50 1N,M50
N ≤ K ≤ N M N \leq K \leq NM NKNM
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 i1个数字之和为 j j j时的方案数,不难得出当我第 i i i个数字确定为 p p p,那么前 i i i个数字之和为 j + p j+p j+p的方案数中一定会包含前 i − 1 i-1 i1个数字之和为 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[i1][jp]
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 1N2×105
1 ≤ A i ≤ N 1 \leq A_i \leq N 1AiN
1 ≤ Q ≤ 2 × 1 0 5 1 \leq Q \leq 2\times 10^5 1Q2×105
1 ≤ L ≤ R ≤ N , 1 ≤ X ≤ N , 1\le L \leq R \leq N, 1 \leq X \leq N, 1LRN,1XN, 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 1iN, 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 1KN300
∣ X i ∣ , ∣ Y i ∣ ≤ 1 0 9 \lvert X_i \rvert, \lvert Y_i \rvert \leq 10^9 Xi,Yi109
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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

registor11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值