2020-09 CSP真题
1. 检测点查询
-
思路:遍历排序即可
#include<bits/stdc++.h>
using namespace std;
int n, xx, yy;
struct AC{
int x, y, num, dis2;
}note[205];
bool cmp(AC a, AC b){
if(a.dis2 == b.dis2) return a.num < b.num;
return a.dis2 < b.dis2;
}
int main(){
cin >> n >> xx >> yy;
for(int i = 1; i <= n; i++){
cin >> note[i].x >> note[i].y;
note[i].num = i;
note[i].dis2 = (note[i].x - xx) * (note[i].x - xx) + (note[i].y - yy) * (note[i].y - yy);
}
sort(note + 1, note + n + 1, cmp);
for(int i = 1; i <= 3; i++)
cout << note[i].num << endl;
return 0;
}
2. 风险人群筛查
-
思路:首先判断一下每个人每个时刻是否在危险区域中,统计每个人连续在高危区域的最长时间即可。
#include<bits/stdc++.h>
using namespace std;
int n, k, t, xl, xr, yd, yu;
int cntj = 0, cntd = 0;
struct AC{
int flg[1005];
}note[25];
int main(){
cin >> n >> k >> t >> xl >> yd >> xr >> yu;
for(int i = 1; i <= n; i++){
memset(note[i].flg, 0, sizeof(note[i].flg));
for(int j = 1; j <= t; j++){
int x, y;
cin >> x >> y;
if(xl <= x && x <= xr && yd <= y && y <= yu) note[i].flg[j] = 1;
}
}
for(int i = 1; i <= n; i++){
int tmp = 0, maxn = 0;
for(int j = 1; j <= t; j++){
if(note[i].flg[j]){
if(note[i].flg[j - 1]) tmp++;
else tmp = 1;
}
else tmp = 0;
maxn = max(maxn, tmp);
}
if(maxn != 0) cntj++;
if(maxn >= k) cntd++;
}
cout << cntj << endl << cntd <<endl;
return 0;
}
3. 点亮数字人生
-
知识点:拓扑排序
-
思路:
- 首先,先根据所有器件排列顺序,形成拓扑排序,可以用拓扑排序判有向图是否成环(按拓扑序将所有有效节点排入队列,若无环则有效节点一定等于所有节点)。
- 接下来,根据拓扑序计算每个器件的值即可。注意由于可能多输入,且不知输入个数,则可用
vector
存所有输入即可。而且拓扑排序的性质决定当前器件前的所有器件一定均已完成计算。
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define VI vector<int>
#define PII pair<int, int>
const int N = 5e2 + 5;
const int M = 1e4 + 5;
int cas, input, gatenum;
int s, In[M][N * 5], Output[M][N], outputnum[M], out[N];
struct AC{
int innum, ru;
string type;
vector<PII> in;
VI edge;
}gate[N];
queue<int> q, order, tmporder;
void init(){ //初始化
rep(i, 1, gatenum){
gate[i].in.clear();
gate[i].edge.clear();
gate[i].ru = 0;
}
while(q.size()) q.pop();
while(order.size()) order.pop();
}
bool topsort(){ //拓扑排序判断有向图是否有环
rep(i, 1, gatenum) if(!gate[i].ru) q.push(i);
while(q.size()){
int tmp = q.front(); q.pop();
order.push(tmp); //存计算顺序
for(auto now : gate[tmp].edge){
gate[now].ru--;
if(!gate[now].ru) q.push(now);
}
}
return order.size() == gatenum;
}
int calc(int snum, vector<PII> in, string type){
int ans = in[0].first == 1 ? In[snum][in[0].second] : out[in[0].second];
if(type == "NOT") return !ans; //非运算只有一个输入,直接输出!!!
rep(i, 1, in.size() - 1){
int now = (in[i].first == 1 ? In[snum][in[i].second] : out[in[i].second]);
if(type == "AND" || type == "NAND") ans &= now;
else if(type == "OR" || type == "NOR") ans |= now;
else if(type == "XOR") ans ^= now;
}
if(type == "NAND" || type == "NOR") return !ans;
else return ans;
}
int main(){
cin >> cas;
while(cas--){
cin >> input >> gatenum;
init();
//存图
rep(i, 1, gatenum){
cin >> gate[i].type >> gate[i].innum;
rep(j, 1, gate[i].innum){
string ss; cin >> ss;
int tmp = stoi(ss.substr(1));
if(ss[0] == 'O'){ //建图
gate[i].ru++;
gate[tmp].edge.push_back(i);
}
gate[i].in.push_back({ss[0] == 'I' ? 1 : 0, tmp}); //存所有输入数据
}
}
//存操作
cin >> s;
rep(i, 1, s) rep(j, 1, input) scanf("%d", &In[i][j]);
rep(i, 1, s){
scanf("%d", &outputnum[i]);
rep(j, 1, outputnum[i]) scanf("%d", &Output[i][j]);
}
//查环
if(!topsort()){
printf("LOOP\n");
continue;
}
//模拟计算
rep(i, 1, s){
tmporder = order; //不能更改order,所以需要tmporder代替计算
memset(out, 0, sizeof(out));
while(tmporder.size()){
int top = tmporder.front(); tmporder.pop();
out[top] = calc(i, gate[top].in, gate[top].type);
}
rep(j, 1, outputnum[i]) printf("%d ", out[Output[i][j]]);
printf("\n");
}
}
}
4. 星际旅行
-
知识点:计算几何
-
思路:在 n n n 维空间中,除距离计算不同,其他都和三维是一样的。发现所有点对只有三种情况
- 当两点所连直线不在黑洞内时
incirc = 0
,两点最短距离为两点直线距离 - 当两点所连直线在黑洞内时
incirc = 1
,考虑两点为端点的线段是否经过黑洞(是否为钝角来判断),- 若不经过,两点最短距离仍为两点直线距离
- 若经过黑洞,则需要切线到黑洞边缘,再沿黑洞边缘到另一个切点。画图得到两个直角边,和一个圆弧。其中直角边用勾股定理计算,圆弧求得对应角度后,用弧长公式计算。
最后统计即可。
- 当两点所连直线不在黑洞内时
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 2e3 + 5;
const db eps = 1e-10;
int n, m;
db r, dis[N][N], ans[N][N];
struct AC{
db dim[105];
}node[N], O;
db distance(AC a, AC b){
db ans = 0;
rep(i, 1, n) ans += (a.dim[i] - b.dim[i]) * (a.dim[i] - b.dim[i]);
return sqrt(ans);
}
db tangent(int num){
return sqrt(dis[0][num] * dis[0][num] - r * r);
}
bool incirc(db a, db b, db c){
db p = (a + b + c) / 2.0;
db s = sqrt(p * (p - a) * (p - b) * (p - c));
db h = 2.0 * s / a;
if(fabs(h - r) < eps) return 0;
else return h < r;
}
db right_tri(int num){
return acos(r / dis[0][num]);
}
db Cos(db a, db b, db c){
return (a * a + b * b - c * c) / (2 * a * b);
}
int main(){
cin >> n >> m >> r;
rep(i, 1, n) scanf("%lf", &O.dim[i]);
rep(i, 1, m) rep(j, 1, n) scanf("%lf", &node[i].dim[j]);
rep(i, 1, m) dis[0][i] = distance(O, node[i]);
rep(i, 1, m){
rep(j, i + 1, m){
dis[i][j] = distance(node[i], node[j]);
if(incirc(dis[i][j], dis[0][i], dis[0][j])){
db anglei = Cos(dis[0][i], dis[i][j], dis[0][j]), anglej = Cos(dis[0][j], dis[i][j], dis[0][i]);
if(anglei < 0 || anglej < 0) ans[i][j] = ans[j][i] = dis[i][j]; //两点在黑洞同侧
else{
db angle = acos(fmax(fmin(Cos(dis[0][i], dis[0][j], dis[i][j]), 1.0), -1.0)); //防止精度问题造成acos越界(最后10分)
angle -= right_tri(i) + right_tri(j);
ans[i][j] = ans[j][i] = tangent(i) + tangent(j) + r * angle;
}
}
else ans[i][j] = ans[j][i] = dis[i][j];
}
}
rep(i, 1, m){
db tmp = 0;
rep(j, 1, m){
if(j == i) continue;
tmp += ans[i][j];
}
printf("%.14lf\n", tmp);
}
}