CSUST选拔赛题解
本鶸鸡于本月10号参加了蔽校的选拔赛,成绩差的死,大部分的题都是赛后花了好长时间才补出来的,其中有些题还是靠QAQorz大佬帮忙才能解决,感谢Qls对我的帮助~接下来就附带上我的暴力题解,大佬们有更好的想法请一定要告诉我啊~
Problem A: 灾区重建
题目链接:http://113.240.233.2:8081/JudgeOnline/problem.php?cid=1015&pid=0
这题求的是最大生成树,我用的是kruskal算法,代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int inf=0x3f3f3f3f; 5 const int maxn=1e6+7; 6 7 int t,n,m; 8 int fa[maxn],r[maxn]; 9 10 struct edge{ 11 int u,v,w; 12 }es[maxn]; 13 14 bool cmp(const edge& e1,const edge& e2){ 15 return e1.w>e2.w; 16 } 17 18 void init(int n){ 19 for(int i=0;i<n;i++){ 20 fa[i]=i; 21 r[i]=0; 22 } 23 } 24 25 int fi(int x){ 26 return fa[x]==x?x:fa[x]=fi(fa[x]); 27 } 28 29 void unite(int x,int y){ 30 int p1=fi(x),p2=fi(y); 31 if(p1==p2) return; 32 if(r[p1]>r[p2]) fa[p2]=p1; 33 else{ 34 fa[p1]=p2; 35 if(r[p1]==r[p2]) r[p2]++; 36 } 37 } 38 39 bool check(int x,int y){ 40 return fi(x)==fi(y); 41 } 42 43 44 int main(){ 45 scanf("%d",&t); 46 for(int k=1;k<=t;k++){ 47 scanf("%d%d",&n,&m); 48 for(int i=0;i<m;i++){ 49 scanf("%d%d%d",&es[i].u,&es[i].v,&es[i].w); 50 } 51 init(n); 52 int ans=inf; 53 sort(es,es+m,cmp); 54 for(int i=0;i<m;i++){ 55 edge e=es[i]; 56 if(!check(e.u,e.v)){ 57 unite(e.u,e.v); 58 ans=min(ans,e.w); 59 } 60 } 61 printf("Case #%d: %d\n",k,ans); 62 } 63 }
Problem B: 洗衣
题目链接:http://113.240.233.2:8081/JudgeOnline/problem.php?cid=1015&pid=1
本题是一个贪心题,不过很多方法会被T,貌似区间取点也会被T。我采用的是借用优先队列,先将所有衣服按照开始时间排序,然后用优先队列对已经入列的衣服的结束时间进行维护。未入列的衣服每次与队列第一个元素进行比较,如果它的开始时间小于第一个元素的结束时间,那么肯定需要新开一台洗衣机;否则,不用问了,把第一个元素踢了,将这件衣服请进队列==!QAQorz大佬说本题可以采用线段树+离散化过,大家坐等她的博客更新此种方法吧,至于她更新时间嘛……emmm,我也不知道~
1 #include <cstdio> 2 #include <queue> 3 #include <algorithm> 4 using namespace std; 5 6 const int maxn=1e5+7; 7 int n; 8 9 struct node{ 10 int s,t; 11 bool operator < (const node& a) const 12 { 13 return t>a.t || (t==a.t && s>a.s); 14 } 15 }l[maxn]; 16 17 bool cmp(const node& x,const node& y){ 18 return x.s==y.s?(x.t<y.t):(x.s<y.s); 19 } 20 21 int main(){ 22 while(~scanf("%d",&n)){ 23 for(int i=0;i<n;i++){ 24 scanf("%d%d",&l[i].s,&l[i].t); 25 } 26 priority_queue<node> q; 27 sort(l,l+n,cmp); 28 if(n==1) printf("1\n"); 29 else{ 30 q.push(l[0]); 31 for(int i=1;i<n;i++){ 32 node f=q.top(); 33 if(l[i].s<f.t){ 34 q.push(l[i]); 35 } 36 else{ 37 q.pop(); 38 q.push(l[i]); 39 } 40 } 41 printf("%d\n",q.size()); 42 } 43 } 44 }
Problem C: 先有durong后有天
题目链接:http://113.240.233.2:8081/JudgeOnline/problem.php?cid=1015&pid=2
具体解释再代码注释内,如果还不懂请在评论区提问,我会尽力为你解答
1 #include <cstdio> 2 #include <queue> 3 #include <algorithm> 4 #include <vector> 5 using namespace std; 6 7 const int inf=0x3f3f3f3f; 8 const int maxn=3007; 9 10 int n,m,s1,e1,s2,e2,t1,t2; 11 int d[maxn][maxn]; 12 13 vector<int> G[maxn]; 14 15 void init(){ 16 for(int i=0;i<maxn;i++){ 17 G[i].clear(); 18 } 19 for(int i=1;i<maxn;i++){ 20 for(int j=1;j<maxn;j++){ 21 if(i==j) d[i][j]=0; 22 else d[i][j]=d[j][i]=inf; 23 } 24 } 25 } 26 27 void bfs(int u){ 28 queue<int> q; 29 q.push(u); 30 while(!q.empty()){ 31 int t=q.front();q.pop(); 32 for(int i=0;i<G[t].size();i++){ 33 int p=G[t][i]; 34 if(d[u][p]>=inf){ 35 d[u][p]=d[u][t]+1; 36 q.push(p); 37 } 38 } 39 } 40 } 41 42 int main(){ 43 while(~scanf("%d%d",&n,&m)){ 44 init(); 45 int a,b; 46 for(int i=0;i<m;i++){ 47 scanf("%d%d",&a,&b); 48 G[a].push_back(b); 49 G[b].push_back(a); 50 } 51 for(int i=1;i<=n;i++){ 52 bfs(i); 53 } 54 scanf("%d%d%d%d%d%d",&s1,&e1,&t1,&s2,&e2,&t2); 55 int ans1=min(d[s1][e1],d[e1][s1]),ans2=min(d[s2][e2],d[e2][s2]); 56 if(ans1>t1 || ans2>t2){ //如果不卖任何一条路都不能达到要求,那么就是不满足题意,输出-1; 57 printf("-1\n"); 58 continue; 59 } 60 else{ 61 int ans=ans1+ans2; 62 for(int i=1;i<=n;i++){ 63 for(int j=1;j<=n;j++){ 64 int tt1=min(d[s1][i]+d[i][j]+d[j][e1],d[e1][i]+d[i][j]+d[j][s1]); //因为我采用的求距离是有方向的,所以还得反过来比价一下; 65 int tt2=min(d[s2][i]+d[i][j]+d[j][e2],d[e2][i]+d[i][j]+d[j][s2]); 66 if(tt1==ans1 && tt2==ans2){ //如果tt1==ans1且tt2==ans2,那么就说明i和j是两条最短路中间重合部分的起点和终点; 67 ans=min(ans,ans1+ans2-d[i][j]); 68 } 69 } 70 } 71 printf("%d\n",m-ans); //将不是最短路经过的路全部卖了就行…… 72 } 73 } 74 }
(对于此题,我想问一句,,durong大佬没钱是不是因为将所有的前都拿去买衣服了?durong大佬对衣服真的是情有独钟钟)
Problem D: 一棵树
题目链接:http://113.240.233.2:8081/JudgeOnline/problem.php?cid=1015&pid=3
题目描述:给你一颗有n个顶点的树。树上有n-1条边,边上的权值c代表一对顶点(u,v)的距离,定义为x到y上的距离,求
数据范围:T<=10,n<=100,000,1<=c<=100,000
Time Limit: 1 Sec Memory Limit: 128 MB
这个题就是一个树上递归,通过找规律找出每条路需要通过的次数,然后在dfs时顺便将结果算出来。寻找规律的流程如下图:
1 #include <cstdio> 2 #include <vector> 3 using namespace std; 4 5 typedef long long ll; 6 const int maxn=1e5+7; 7 int t,n; 8 ll ed[maxn],sum[maxn]; //ed是用来存放第i点的子节点数+1; 9 10 struct edge{ 11 int v; 12 ll w; 13 edge(int v=0,ll w=0):v(v),w(w){} 14 }; 15 16 vector<edge> G[maxn]; 17 18 void init(){ 19 for(int i=0;i<maxn;i++){ 20 ed[i]=0; 21 sum[i]=0; 22 G[i].clear(); 23 } 24 } 25 26 void dfs(int u,int p){ 27 ed[u]=1; 28 for(int i=0;i<G[u].size();i++){ 29 int v=G[u][i].v; 30 ll w=G[u][i].w; 31 if(v!=p){ 32 dfs(v,u); 33 ed[u]+=ed[v]; 34 sum[u]+=sum[v]+ed[v]*(n-ed[v])*w; 35 } 36 } 37 } 38 39 40 int main(){ 41 scanf("%d",&t); 42 while(t--){ 43 init(); 44 scanf("%d",&n); 45 int u,v; 46 ll w; 47 for(int i=1;i<n;i++){ 48 scanf("%d%d%lld",&u,&v,&w); 49 G[u].push_back(edge(v,w)); 50 G[v].push_back(edge(u,w)); 51 } 52 dfs(1,-1); 53 printf("%lld\n",sum[1]); 54 } 55 }
Problem E: 杜荣NB
题目链接:http://113.240.233.2:8081/JudgeOnline/problem.php?cid=1015&pid=4
这题就是xjb打表就行,因为给的时限有5s。比赛时因为题面错了,将x的上限设为1e8,这样需要数位dp好像,前面也说过了,我的dp非常菜,所以当时挣扎了一段时间就放弃了==!
1 #include <cstdio> 2 #include <set> 3 #include <algorithm> 4 using namespace std; 5 6 const int maxn=1e7+7; 7 int b,t,x; 8 int rk[maxn]; 9 set<int> s; 10 11 int main(){ 12 while(~scanf("%d",&b)){ 13 int r=0; 14 s.clear(); 15 for(int i=0;i<=1e7;i++){ 16 int m=i,sum=0; 17 while(m){ 18 sum+=m%10; 19 m/=10; 20 } 21 if(sum==b){ 22 rk[r++]=i; 23 s.insert(i); 24 } 25 } 26 scanf("%d",&t); 27 while(t--){ 28 scanf("%d",&x); 29 if(!s.count(x)){ 30 printf("durongNB\n"); 31 } 32 else{ 33 int u=r-1,l=0; 34 if(rk[u]==x){ 35 printf("%d\n",u+1); 36 } 37 else if(rk[0]==x){ 38 printf("1\n"); 39 } 40 else{ 41 int mid; 42 while(u>l){ 43 mid=(u+l)/2; 44 if(rk[mid]==x){ 45 printf("%d\n",mid+1); 46 break; 47 } 48 else if(rk[mid]>x){ 49 u=mid; 50 } 51 else{ 52 l=mid; 53 } 54 } 55 } 56 } 57 } 58 } 59 }
Problem F: 挑战迷宫
题目链接:http://113.240.233.2:8081/JudgeOnline/problem.php?cid=1015&pid=5
此题是一个比较裸(或者说是纯?)的LCA,至于算距离嘛,在dfs时顺便算出当前节点到根节点的距离,之后就将询问的两个节点到根节点的距离相加再减去二者最近公共祖宗节点到根节点的距离即可,代码实现如下:
1 #include <cstdio> 2 #include <vector> 3 using namespace std; 4 5 const int maxn=1e5+7; 6 int n,m; 7 int pa[maxn][30],deep[maxn],cost[maxn]; 8 9 struct edge{ 10 int v,l; 11 edge(int v=0,int l=0):v(v),l(l){} 12 }; 13 14 vector<edge> G[maxn]; 15 16 void dfs(int id,int p,int d){ 17 pa[id][0]=p; 18 deep[id]=d; 19 for(int i=0;i<G[id].size();i++){ 20 int a=G[id][i].v; 21 if(a!=p){ 22 cost[a]=cost[id]+G[id][i].l; 23 dfs(a,id,d+1); 24 } 25 } 26 } 27 28 void lca(){ 29 for(int i=1;i<=n;i++){ 30 for(int j=1;(1<<j)<=n;j++){ 31 pa[i][j]=-1; 32 } 33 } 34 for(int j=1;(1<<j)<=n;j++){ 35 for(int i=1;i<=n;i++){ 36 if(pa[i][j-1]!=-1){ 37 pa[i][j]=pa[pa[i][j-1]][j-1]; 38 } 39 } 40 } 41 } 42 43 int query(int u,int v){ 44 if(deep[u]<deep[v]) swap(u,v); 45 int dd; 46 for(dd=0;(1<<(dd+1))<=deep[u];dd++); 47 for(int i=dd;i>=0;i--){ 48 if(deep[u]-(1<<i)>=deep[v]){ 49 u=pa[u][i]; 50 } 51 } 52 if(u==v) return u; 53 for(int i=dd;i>=0;i--){ 54 if(pa[u][i]!=-1 && pa[u][i]!=pa[v][i]){ 55 u=pa[u][i]; 56 v=pa[v][i]; 57 } 58 } 59 return pa[u][0]; 60 } 61 62 int main(){ 63 while(~scanf("%d",&n)){ 64 int u,v,w; 65 for(int i=1;i<n;i++){ 66 scanf("%d%d%d",&u,&v,&w); 67 G[u].push_back(edge(v,w)); 68 G[v].push_back(edge(u,w)); 69 } 70 dfs(1,-1,0); 71 lca(); 72 scanf("%d",&m); 73 for(int i=0;i<m;i++){ 74 scanf("%d%d",&u,&v); 75 printf("%d\n",cost[u]+cost[v]-2*cost[query(u,v)]); 76 } 77 } 78 }
Problem G: 括号匹配
题目链接:http://113.240.233.2:8081/JudgeOnline/problem.php?cid=1015&pid=6
此题……QAQorz教我的,我的dp菜的一匹,只会简单的01背包系列,我只解释一下这个dp的状态算了,dp[i][j]存的是第i个位置第j层的方案数(表述好像不太清楚,那就自行体会吧,以后再来填坑算了-_-!),代码实现如下:
1 #include <cstdio> 2 #include <cstring> 3 4 const int maxn=3007; 5 const int mod=1e9+7; 6 int t; 7 char s[maxn]; 8 int dp[maxn][maxn]; 9 10 int main(){ 11 scanf("%d",&t); 12 while(t--){ 13 scanf("%s",s); 14 int len=strlen(s); 15 if(len%2){ 16 printf("0\n"); 17 continue; 18 } 19 memset(dp,0,sizeof(dp)); 20 dp[0][0]=1; 21 for(int i=0;i<len;i++){ 22 for(int j=0;j<len/2+1;j++){ 23 if(s[i]=='('){ 24 if(j>0) dp[i+1][j]=dp[i][j-1]; 25 else dp[i+1][j]=0; 26 } 27 else if(s[i]==')'){ 28 if(j<len/2) dp[i+1][j]=dp[i][j+1]; 29 else dp[i+1][j]=0; 30 } 31 else{ 32 dp[i+1][j]=0; 33 if(j>0) dp[i+1][j]=dp[i][j-1]+dp[i][j+1]; 34 else dp[i+1][j]=dp[i+1][j]; 35 if(j<len/2) dp[i+1][j]=dp[i][j+1]+dp[i][j-1]; 36 else dp[i+1][j]=dp[i+1][j]; 37 } 38 dp[i+1][j]%=mod; 39 } 40 } 41 printf("%d\n",dp[len][0]%mod); 42 } 43 }
Problem H: 逃出监狱
题目链接:http://113.240.233.2:8081/JudgeOnline/problem.php?cid=1015&pid=7
题目描述:给你一个n*m的图,地图上'.'代表可以走的地方,而'#'代表障碍物不能走,
'A'代表小偷,'B'代表警察,'E'代表出口。每个位置可以向(上,下,左,
右)四个方向走一格,花费一个单位时间,现在给出小偷,警察和出口所在
位置,警察为了捉住小偷会先到出口的位置守株待兔,如果警察在小偷之前
或同时到达出口,或者小偷到达不了出口,输出"No",否则输出"Yes"。
数据范围:T<=50,n<=500,m<=500
Time Limit: 1 Sec Memory Limit: 128 MB
这个因为是两个起点一个终点,所以完全可以从终点开始搜,完全没必要跑两个bfs。但是比赛时一直在纠结警察会不会在路上抓到小偷,然后思考了半小时,默默地看着好几个人A了,最后实在没办法了,就试了下不能在路上抓到小偷,,然后就A了,然后就开始思考为啥自己那么喜欢多想,不然就是1A了-_-!
1 #include <cstdio> 2 #include <queue> 3 #include <cstring> 4 using namespace std; 5 6 const int inf=0x3f3f3f3f; 7 int w,h,ans; 8 int sx,sy,k; 9 char mp[25][25]; 10 int vis[25][25]; 11 12 struct node{ 13 int x,y,step; 14 }nw,nxt; 15 16 int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1}; 17 18 int bfs(int x,int y){ 19 nw.x=x,nw.y=y,nw.step=0; 20 vis[y][x]=1; 21 queue<node> q; 22 q.push(nw); 23 while(!q.empty()){ 24 nw=q.front();q.pop(); 25 if(mp[nw.y][nw.x]=='*'){ 26 memset(vis,0,sizeof(vis)); 27 vis[nw.y][nw.x]=1; 28 mp[nw.y][nw.x]='.'; 29 if(k==1){ 30 k--; 31 return nw.step; 32 } 33 k--; 34 return nw.step+bfs(nw.x,nw.y); 35 } 36 for(int i=0;i<4;i++){ 37 nxt.x=nw.x+dx[i],nxt.y=nw.y+dy[i]; 38 if(nxt.x>=0 && nxt.x<h && nxt.y>=0 && nxt.y<w && vis[nxt.y][nxt.x]==0 && mp[nxt.y][nxt.x]!='x'){ 39 nxt.step=nw.step+1; 40 vis[nxt.y][nxt.x]=1; 41 q.push(nxt); 42 } 43 } 44 } 45 return 0; 46 } 47 48 int main(){ 49 while(~scanf("%d%d",&h,&w)){ 50 if(w==0 && h==0) break; 51 for(int i=0;i<w;i++){ 52 scanf("%s",mp[i]); 53 } 54 k=0,ans=0; 55 for(int i=0;i<w;i++){ 56 for(int j=0;j<h;j++){ 57 if(mp[i][j]=='o'){ 58 sx=j,sy=i; 59 } 60 if(mp[i][j]=='*'){ 61 k++; 62 } 63 } 64 } 65 ans=bfs(sx,sy); 66 if(k>0){ 67 printf("-1\n"); 68 } 69 else{ 70 printf("%d\n",ans); 71 } 72 } 73 }
Problem I: 简单题
题目链接:http://113.240.233.2:8081/JudgeOnline/problem.php?cid=1015&pid=8
题目描述:给你N个数Q次操作。其中每次操作包括1.从l到r的所有元素都加x和查询,2.求第l个元素到第r个元素间的数据的平均值,保留两位小数
数据范围:N <= 100000,Q <= 100000,数列中的每个元素 <= 100,0<=X<=100
Time Limit: 1 Sec Memory Limit: 128 MB
这题就是一个纯裸的线段树,但是比赛时由于带的板子出了问题,没发现&&忘记开long long猛WA3发-_-!,还好及时发现,最后竟然还拿了本题的一血(雾。所以这题就是带副没有错误的好板子(-_-!)和记得开long long就行~
1 #include <cstdio> 2 3 typedef long long ll; 4 const int maxn=1e5+7; 5 6 int t,n,q; 7 int a[maxn]; 8 9 struct node{ 10 int l,r; 11 ll sum,lazy; 12 }tree[maxn*4]; 13 14 void push_down(int i){ 15 if(tree[i].lazy!=0 && tree[i].l!=tree[i].r){ 16 ll x=tree[i].lazy; 17 tree[i*2].lazy+=x; 18 tree[i*2+1].lazy+=x; 19 tree[i*2].sum+=x*(tree[i*2].r-tree[i*2].l+1); 20 tree[i*2+1].sum+=x*(tree[i*2+1].r-tree[i*2+1].l+1); 21 tree[i].lazy=0; 22 } 23 return; 24 } 25 26 void push_up(int i){ 27 tree[i].sum=tree[i*2].sum+tree[i*2+1].sum; 28 return; 29 } 30 31 void build(int i,int l,int r){ 32 tree[i].l=l,tree[i].r=r; 33 tree[i].lazy=0; 34 if(l==r){ 35 tree[i].sum=a[l]; 36 return; 37 } 38 int mid=(l+r)/2; 39 build(i*2,l,mid); 40 build(i*2+1,mid+1,r); 41 push_up(i); 42 } 43 44 void update(int i,int l,int r,ll x){ 45 push_down(i); 46 if(tree[i].r==r &&tree[i].l==l){ 47 tree[i].sum+=x*(r-l+1); 48 tree[i].lazy+=x; 49 return; 50 } 51 int mid=(tree[i].l+tree[i].r)/2; 52 if(r<=mid) update(i*2,l,r,x); 53 else if(l>mid) update(i*2+1,l,r,x); 54 else{ 55 update(i*2,l,mid,x); 56 update(i*2+1,mid+1,r,x); 57 } 58 push_up(i); 59 } 60 61 ll query(int i,int l,int r){ 62 push_down(i); 63 if(tree[i].l==l &&tree[i].r==r){ 64 return tree[i].sum; 65 } 66 int mid=(tree[i].l+tree[i].r)/2; 67 if(l>mid) return query(i*2+1,l,r); 68 else if(r<=mid) return query(i*2,l,r); 69 else{ 70 return query(i*2,l,mid)+query(i*2+1,mid+1,r); 71 } 72 } 73 74 int main(){ 75 scanf("%d",&t); 76 while(t--){ 77 scanf("%d",&n); 78 for(int i=1;i<=n;i++){ 79 scanf("%d",&a[i]); 80 } 81 build(1,1,n); 82 scanf("%d",&q); 83 int x; 84 for(int i=0;i<q;i++){ 85 scanf("%d",&x); 86 if(x){ 87 int l,r; 88 scanf("%d%d",&l,&r); 89 double ans=1.0*query(1,l,r)/(r-l+1); 90 printf("%.2f\n",ans); 91 } 92 else{ 93 int l,r; 94 ll s; 95 scanf("%d%d%lld",&l,&r,&s); 96 update(1,l,r,s); 97 } 98 } 99 } 100 }