试题内容请前往CCF官网查看:
CCF-CSP计算机软件能力认证考试
http://118.190.20.162/home.page
这是一份以C++代码编写的CSP 专业组 202212题解。
阅读本题解前,您应当了解下列知识:
请注意这不是CSP-S/J的中学生竞赛的题解。
现将模拟测试系统中的得分列举如下:
题目 | 得分 | 时间 | 内存 |
---|---|---|---|
现值计算 | 100 | 15ms | 3.136MB |
训练计划 | 100 | 15ms | 2.988MB |
JPEG 解码 | 100 | 15ms | 2.921MB |
聚集方差 | 100 | 1.828s | 192.9MB |
星际网络 | - | - | - |
最后一题还没有看,日后更新。
1. 现值计算
提示很充足,按照提示完成题目即可。
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;double s,r,x;
cin>>n>>r>>s;
for(int i=1;i<=n;++i){
cin>>x;
s+=x*pow(1+r,-i);
}
cout<<s<<endl;
return 0;
}
2. 训练计划
训练计划构成一棵树。最早开始时间从根往叶子递推。最迟开始时间从从叶子往根递推,也就递归求解。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=370,MAXM=110;
bool vis[MAXN];
int early[MAXM],late[MAXM],p[MAXM],t[MAXM],n;
vector<int> gl[MAXM];
int check_early(int i){
if(early[i]) return early[i];
if(p[i]==0) return early[i]=1;
return early[i]=check_early(p[i])+t[p[i]];
}
int check_late(int i){
if(i==0) return n+1;
if(late[i]<=n) return late[i];
int lt=n+1;
for(auto ch:gl[i]){lt=min(lt,check_late(ch));}
return late[i]=lt-t[i];
}
int main(){
int m,x;
cin>>n>>m;
for(int i=1;i<=m;++i){
cin>>p[i];
if(p[i])gl[p[i]].push_back(i);
}
for(int i=1;i<=m;++i)cin>>t[i],late[i]=n+1;
late[0]=n+1;
for(int i=1;i<=m;++i)check_early(i);
for(int i=1;i<=m;++i)cout<<early[i]<<" ";
cout<<endl;
bool f=true;
for(int i=1;i<=m;++i)
if(check_late(i)<=0){
f=false;break;
}
if(f){
for(int i=1;i<=m;++i)cout<<late[i]<<" ";
cout<<endl;
}
return 0;
}
3. JPEG 解码
大型模拟题,没有什么难度,照做即可。
值得一提的是蛇形输入,这个稍微动动脑筋。实在不行打个表,指示每个数的输入位置,毕竟只有8x8=64个格子。
#include<bits/stdc++.h>
using namespace std;
const double pi8=acos(-1)/8.0;
double alpha(int u){return u==0?sqrt(0.5):1;}
int cast(int x){
if(x<0) return 0;
if(x>255) return 255;
return x;
}
class matrix{
int a[8][8];
public:
matrix(){
for(int i=0;i<8;++i)
for(int j=0;j<8;++j)
a[i][j]=0;
}
void read_mat(){
for(int i=0;i<8;++i)
for(int j=0;j<8;++j)
cin>>a[i][j];
}
void read_scan_data(int n){
int i=0,j=0,k=1;
while(n--){
cin>>a[i][j];
if(k==1)
if(j==7) ++i,k=-1;
else if(i==0) ++j,k=-1;
else --i,++j;
else//k==-1
if(i==7) ++j,k=1;
else if(j==0) ++i,k=1;
else ++i,--j;
}
}
matrix& operator*=(matrix &m){
for(int i=0;i<8;++i)
for(int j=0;j<8;++j)
a[i][j]*=m.a[i][j];
return (*this);
}
matrix idct(){
matrix ret;
for(int i=0;i<8;++i){
for(int j=0;j<8;++j){
double s=0;
for(int u=0;u<8;++u)
for(int v=0;v<8;++v)
s+=alpha(u)*alpha(v)*a[u][v]*cos(pi8*(i+0.5)*u)*cos(pi8*(j+0.5)*v);
ret.a[i][j]=cast(round(s/4.0+128));
}
}
return ret;
}
};
int main(){
matrix q,m;
q.read_mat();
int n;cin>>n;
int T;cin>>T;
m.read_scan_data(n);
if(T==0){m.print();return 0;}
m*=q;
if(T==1){m.print();return 0;}
m.idct().print();
return 0;
}
4. 聚集方差
子任务1,2: 对于一棵子树,我们维护一个map
,指示其中每种数有几个,然后计算“聚集方差”即可。向上递推时,暴力合并几个子树再加一个根节点。时间复杂度
O
(
n
2
log
n
)
O(n^2\log n)
O(n2logn)(
log
\log
log是由map
查询操作带来的),可得40分。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll sqr(int x){return (ll)x*x;}
const int MAXN=3e5+10;
struct sta{
int min_dist,count;
sta():min_dist(-1),count(1){}
sta(int md,int cnt):min_dist(md),count(cnt){}
void inc(int c=1){count+=c;if(count>=2) min_dist=0;}
};
typedef map<int,sta> dict;
typedef dict::iterator dictit;
class statistics{//个数统计器
int cnt;ll ga;
dict mp;
int __1st() {return mp.begin()->first;}//得到map中的第一个数
pair<vector<int>,vector<dictit> > __dist(dictit it) {//对于给定的数,计算它的最小距离,以及左右2个数的最小距离
vector<int> ret;vector<dictit> retp(3,mp.end());
int x=it->first,d=INT_MAX;
if(it==mp.begin()){
ret.push_back(-1);
}else{
auto itl=it;--itl;
if(x-itl->first<d){
d=x-itl->first;
retp[2]=itl;
}
ret.push_back(itl->second.min_dist);
retp[0]=itl;
}
auto end=mp.end();--end;
if(it==end){
ret.push_back(-1);
}else{
auto itr=it;++itr;
if(itr->first-x<d){
d=itr->first-x;
retp[2]=itr;
}
ret.push_back(itr->second.min_dist);
retp[1]=itr;
}
ret.push_back(d);
return {ret,retp};//{左侧数的最小距离,右侧数的最小距离,该数的最小距离},{左侧数的迭代器,右侧数的迭代器,最小距离对应数的迭代器}
}
public:
statistics(int x):cnt(1),ga(0){mp[x]=sta(-1,1);}
void insert(int x,int x_cnt){//插入x_cnt个x
if(cnt==1) {
int y=__1st(),d=abs(x-y);
if(d==0) mp[x]=sta(0,2),ga=0;
else mp[x]=sta(d,1),mp[y]=sta(d,1),ga=2*sqr(d);
--x_cnt;++cnt;
if(x_cnt==0) return;
}
auto it=mp.find(x);
if(it!=mp.end()){
if(it->second.count==1) ga-=sqr(it->second.min_dist);
it->second.inc(x_cnt);
}else{
it=mp.insert({x,sta(-1,x_cnt)}).first;
auto dsp=__dist(it);
int dl=dsp.first[0],dr=dsp.first[1],d=dsp.first[2];
if(x_cnt>1) d=0;
it->second.min_dist=d;
auto pl=dsp.second[0],pr=dsp.second[1];
int l,r;
if(dl!=-1 && dl>x-(l=pl->first)){
ga+=sqr(x-l)-sqr(dl);
pl->second.min_dist=x-l;
}
if(dr!=-1 && dr>(r=pr->first)-x){
ga+=sqr(r-x)-sqr(dr);
pr->second.min_dist=r-x;
}
ga+=sqr(d);
}
cnt+=x_cnt;
}
void insert(statistics &st){//暴力合并子树
for(auto p:st.mp)insert(p.first,p.second.count);
}
ll get_ga(){return ga;}
};
struct node{
int fa,val;ll ga;
vector<int> son;
};
class tree{
int n;
vector<node> a;
public:
tree(int _n):n(_n),a(_n+1){}
void read(){
int f;
for(int i=2;i<=n;++i){
cin>>f;a[i].fa=f;
a[f].son.push_back(i);
}
for(int i=1;i<=n;++i)cin>>a[i].val;
}
int sz(int u){return a[u].son.size()+1;}
int v(int u){return a[u].val;}
ll operator[](int u){return a[u].ga;}
statistics solve(int u){
statistics st(v(u));
if(sz(u)==1) {
a[u].ga=0;
return st;
}
for(auto v:a[u].son){
auto ret=solve(v);
st.insert(ret);
}
a[u].ga=st.get_ga();
return st;
}
};
int main(){
ios::sync_with_stdio(false);
int n;
cin>>n;
tree tr(n);
tr.read();
tr.solve(1);
for(int i=1;i<=n;++i) cout<<tr[i]<<endl;
return 0;
}
完整解: 上一个方法慢就慢在两个子树合并的时间复杂度为 O ( n ) O(n) O(n),如果我们能够采取某种方法 O ( 1 ) O(1) O(1)或者 O ( log n ) O(\log n) O(logn)合并子树,那么问题就解决了。
如果你熟练的掌握树链剖分(这里特指轻重链剖分),那么你应该已经想到了解决方案:
总是以重儿子的map
为主,然后将轻儿子的map
合并到重儿子的map
上。
证明: 利用轻重链剖分的性质,一个点到根路径上不超过 log n \log n logn条轻边,也就是说,一个点最多会被暴力合并 log n \log n logn次。
这种方法被称为树上启发式合并。时间复杂度 O ( n log 2 n ) O(n \log^2 n) O(nlog2n)。
事实上,相比40分的代码,以下代码没有很大的改动,仅仅增加了dfs1
函数用于寻找重儿子,并对solve
递归函数进行了简要修改:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll sqr(int x){return (ll)x*x;}
const int MAXN=3e5+10;
struct sta{
int min_dist,count;
sta():min_dist(-1),count(1){}
sta(int md,int cnt):min_dist(md),count(cnt){}
void inc(int c=1){
count+=c;
if(count>=2) min_dist=0;
}
};
typedef map<int,sta> dict;
typedef dict::iterator dictit;
class statistics{//个数统计器
int cnt;ll ga;
dict mp;
int __1st() {return mp.begin()->first;}
pair<vector<int>,vector<dictit> > __dist(dictit it) {
vector<int> ret;vector<dictit> retp(3,mp.end());
int x=it->first,d=INT_MAX;
if(it==mp.begin()){
ret.push_back(-1);
}else{
auto itl=it;--itl;
if(x-itl->first<d){
d=x-itl->first;
retp[2]=itl;
}
ret.push_back(itl->second.min_dist);
retp[0]=itl;
}
auto end=mp.end();--end;
if(it==end){
ret.push_back(-1);
}else{
auto itr=it;++itr;
if(itr->first-x<d){
d=itr->first-x;
retp[2]=itr;
}
ret.push_back(itr->second.min_dist);
retp[1]=itr;
}
ret.push_back(d);
return {ret,retp};//{左侧数的最小距离,右侧数的最小距离,该数的最小距离},{左侧数的迭代器,右侧数的迭代器,最小距离对应数的迭代器}
}
public:
statistics(int x):cnt(1),ga(0){mp[x]=sta(-1,1);}
void insert(int x,int x_cnt=1){
if(cnt==1) {
int y=__1st(),d=abs(x-y);
if(d==0) mp[x]=sta(0,2),ga=0;
else mp[x]=sta(d,1),mp[y]=sta(d,1),ga=2*sqr(d);
--x_cnt;++cnt;
if(x_cnt==0) return;
}
auto it=mp.find(x);
if(it!=mp.end()){
if(it->second.count==1) ga-=sqr(it->second.min_dist);
it->second.inc(x_cnt);
}else{
it=mp.insert({x,sta(-1,x_cnt)}).first;
auto dsp=__dist(it);
int dl=dsp.first[0],dr=dsp.first[1],d=dsp.first[2];
if(x_cnt>1) d=0;
it->second.min_dist=d;
auto pl=dsp.second[0],pr=dsp.second[1];
int l,r;
if(dl!=-1 && dl>x-(l=pl->first)){
ga+=sqr(x-l)-sqr(dl);
pl->second.min_dist=x-l;
}
if(dr!=-1 && dr>(r=pr->first)-x){
ga+=sqr(r-x)-sqr(dr);
pr->second.min_dist=r-x;
}
ga+=sqr(d);
}
cnt+=x_cnt;
}
void insert(statistics &st){
for(auto p:st.mp)insert(p.first,p.second.count);
}
ll get_ga(){return ga;}
};
struct node{
int fa,val;ll ga;
vector<int> son;
int sz,hson;
};
class tree{
int n;
vector<node> a;
void dfs1(int u,int f) { //寻找重儿子
a[u].sz=1;
for(auto v:a[u].son) {
if(v==f)continue;
dfs1(v,u);
a[u].sz+=a[v].sz;
if(a[a[u].hson].sz<a[v].sz) a[u].hson=v;
}
}
public:
tree(int _n):n(_n),a(_n+1){}
void read(){
int f;
for(int i=2;i<=n;++i){
cin>>f;a[i].fa=f;
a[f].son.push_back(i);
}
for(int i=1;i<=n;++i)cin>>a[i].val;
dfs1(1,0);
}
int sz(int u){return a[u].son.size()+1;}
int hson(int u){return a[u].hson;}
int v(int u){return a[u].val;}
ll operator[](int u){return a[u].ga;}
statistics solve(int u){
if(sz(u)==1) {
a[u].ga=0;
return statistics(v(u));
}
statistics st=solve(hson(u));
for(auto v:a[u].son){
if(v==hson(u))continue;
auto ret=solve(v);
st.insert(ret);
}
st.insert(v(u));
a[u].ga=st.get_ga();
return st;
}
};
int main(){
ios::sync_with_stdio(false);
int n;
cin>>n;
tree tr(n);
tr.read();
tr.solve(1);
for(int i=1;i<=n;++i) cout<<tr[i]<<endl;
return 0;
}
5. 星际网络
(日后更新)