CSP202203
01 未初始化警告
题目
n个赋值语句,判断每个右值出现时是否未被初始化。
- 被初始化:当条赋值语句的右值为变量;右值在之前的赋值语句中作为左值出现过(不论那条语句的右值是否被初始化)。
标签
模拟?
输入
第一行输入两个整数n、k,分别代表变量的个数 和 赋值语句的个数。
之后k行,每行输入两个整数 x i , y i x_i,~y_i xi, yi,代表赋值语句左、右值变量的下标。
输出
输出一行,一个整数,代表n条赋值语句中左值未被初始化的语句条数。
数据范围
-
50%的数据满足 0 < n , k ≤ 1000 0<n,k\le1000 0<n,k≤1000
-
所有数据满足 0 < n , k ≤ 1 0 5 0<n,k\leq10^5 0<n,k≤105
-
1 ≤ x i ≤ n , 0 ≤ y i ≤ n 1\leq x_i\le n,~0\leq y_i\leq n 1≤xi≤n, 0≤yi≤n
思路
开一个足够大的数组,记录每个变量是否初始化,然后统计未被初始化的语句的条数。
注意
- 后k行输入为变量的下标,因此总范围为 [ 0 , n ] [0, n] [0,n]
- 0为常量,没有未初始化的情况
- 不考虑某变量第一次作为左值出现时,右值是否已被初始化
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+7;
bool vis[N];
int main() {
int n, k;
cin >> n >> k;
int l, r, cnt = 0;
vis[0] = true;
for (int i = 0; i < k; ++i) {
cin >> l >> r;
if (!vis[r]) cnt++;
vis[l] = true;
}
cout << cnt;
return 0;
}
02 何以出游
题目
t时刻核酸检测,t+k时刻获得核酸检测结果。有n个地点,地点 i i i 需要有 [ t i − c i + 1 , t i ] [t_i-c_i+1, t_i] [ti−ci+1,ti]时刻内的核酸证明,求能去多少景点。
标签
差分;前缀和;区间计算。
输入
第一行输入三个空格分隔的整数 n m k n~m~k n m k,分别代表景点个数、查询个数、等待核酸结果所需时间。
接下来n行,每行输出两个整数 t i c i t_i~c_i ti ci,分别代表景点最早进入时间,需要持有 c i c_i ci时间内的核酸检测结果。
接下来m行,每行输入一个整数q,代表核酸检测时间。
输出
输出m行,每行输出一个整数。
对于每个查询,输出可以选择的景点个数。
数据范围
- 见CSP网站
思路
对于景点 ( t i , c i ) (t_i,~c_i) (ti, ci),需要持有 [ t i − c i + 1 , t i ] [t_i-c_i+1,t_i] [ti−ci+1,ti]时刻内的核酸证明,即 t i − c i + 1 ≤ q + k ≤ t i t_i-c_i+1\leq q+k\leq t_i ti−ci+1≤q+k≤ti,只要统计 q + k q+k q+k在这样的区间内的个数。
- 如何统计个数?
- 对于每次查询,遍历所有景点,时间复杂度 O ( n m ) O(nm) O(nm)
- 考虑到
t
i
和
c
i
t_i和c_i
ti和ci都已经给定,在q+k范围不大的情况下,可以提前将每个q+k对应可去的景点个数求出。
- 题目中q+k的数量级与n相同,在 1 0 5 10^5 105数量级
- 使用区间端点表示左闭右开区间
- 左端点记为1,右端点记为-1
- 可以认为:进入区间时为1,离开区间时为-1
- f ( x ) : = x 所在区间个数 , f(x):=x所在区间个数, f(x):=x所在区间个数, 这种表示方法下,x处的值为 f ( x ) − f ( x − 1 ) f(x)-f(x-1) f(x)−f(x−1),后向差分。
- 如何计算 f ( x ) f(x) f(x)?使用有限积分,这里等价于前缀和。
- 为什么使用左闭右开区间?
- 良好的可加性: [ l e f t , m i d ) + [ m i d , r i g h t ) = [ l e f t , r i g h t ) [left,~mid)+[mid,~right)=[left,right) [left, mid)+[mid, right)=[left,right)
- 容易计算元素个数: [ l e f t , r i g h t ) 中的元素个数 = r i g h t − l e f t [left,right)中的元素个数=right-left [left,right)中的元素个数=right−left
- 积分时的无穷小量在此不能省略,在使用分部积分法时,这种书写方式更简便。
- 时间复杂度为 O ( n ) O(n) O(n)
注意
- q+k的范围有3e5
- 注意边界条件
- 注意使用左闭右开区间
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5+7;
int t[N], c[N], q;
int avail[N];
int main() {
cin.tie(0);
ios::sync_with_stdio(false);
int n, m, k;
cin >> n >> m >> k;
int fir = 0;
for (int i = 0; i < n; ++i) {
cin >> t[i] >> c[i];
avail[t[i]+1]--;
if (t[i] - c[i] + 1 >= 0) {
avail[t[i]-c[i]+1]++;
} else {
fir++;
}
}
avail[0] += fir;
for (int i = 1; i < N; ++i) {
avail[i] += avail[i-1];
}
for (int i = 0; i < m; ++i) {
cin >> q;
q += k;
cout << avail[q] << endl;
}
return 0;
}
< N; ++i) {
avail[i] += avail[i-1];
}
for (int i = 0; i < m; ++i) {
cin >> q;
q += k;
cout << avail[q] << endl;
}
return 0;
}