模拟赛第二弹,这次做的好多了~
T1
题意就是给一棵树,求点对之间的距离
因为n非常小(<1000),所以直接暴力bfs.....
然而我为了锻炼手速 其实是没看数据 打了树剖【雾】
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 struct edge{ 7 int to,w,next; 8 }e[2010]; 9 int n,q,times=1,first[1010],fa[1010],son[1010],size[1010],dep[1010]; 10 int wei[1010],belong[1010],pos[510][1010],lian[1010],top[1010]; 11 int a[1010][8010],nb[510],len[1010]; 12 bool vis[1010]; 13 void dfs1(int k,int f,int weight){ 14 int i,u,maxn=0,tmp=0; 15 fa[k]=f;vis[k]=1;dep[k]=dep[f]+1;size[k]=1; 16 len[k]=weight; 17 for(i=first[k];~i;i=e[i].next){ 18 u=e[i].to; 19 if(!vis[u]){ 20 dfs1(u,k,e[i].w); 21 size[k]+=size[u]; 22 if(size[u]>maxn){ 23 maxn=size[u];tmp=u; 24 } 25 } 26 } 27 son[k]=tmp; 28 return; 29 } 30 void dfs2(int k,int t,int cnt){ 31 int i,u;nb[times]++; 32 belong[k]=times;pos[times][cnt]=k;wei[k]=cnt;lian[k]=lian[fa[t]]+1; 33 vis[k]=1;top[k]=t; 34 if(son[k]){ 35 dfs2(son[k],t,cnt+1); 36 } 37 for(i=first[k];~i;i=e[i].next){ 38 u=e[i].to; 39 if(!vis[u]){ 40 ++times; 41 dfs2(u,u,1); 42 } 43 } 44 } 45 void build(int l,int r,int num,int t){ 46 if(l==r){ 47 a[t][num]=len[pos[t][l]];return; 48 } 49 int mid=(l+r)>>1; 50 build(l,mid,num<<1,t);build(mid+1,r,(num<<1)+1,t); 51 a[t][num]=a[t][num<<1]+a[t][(num<<1)+1]; 52 return; 53 } 54 int find(int ql,int qr,int l,int r,int num,int t){ 55 if(qr<l||ql>r) return 0; 56 if(l>=ql&&r<=qr) return a[t][num]; 57 int mid=(l+r)>>1,re=0; 58 if(mid>=ql) re+=find(ql,qr,l,mid,num<<1,t); 59 if(mid<qr) re+=find(ql,qr,mid+1,r,(num<<1)+1,t); 60 return re; 61 } 62 void swap(int &x,int &y){ 63 x^=y;y^=x;x^=y; 64 } 65 int query(int l,int r){ 66 int re=0; 67 if(lian[l]>lian[r]) swap(l,r); 68 while(lian[r]!=lian[l]){ 69 re+=find(wei[top[r]],wei[r],1,nb[belong[r]],1,belong[r]); 70 r=fa[top[r]]; 71 } 72 while(top[l]!=top[r]){ 73 re+=find(wei[top[l]],wei[l],1,nb[belong[l]],1,belong[l]); 74 l=fa[top[l]]; 75 re+=find(wei[top[r]],wei[r],1,nb[belong[r]],1,belong[r]); 76 r=fa[top[r]]; 77 } 78 if(l==r) return re; 79 if(dep[l]>dep[r]) swap(l,r); 80 return re+find(wei[l],wei[r],1,nb[belong[l]],1,belong[l])-len[l]; 81 } 82 int main(){ 83 freopen("pwalk.in","r",stdin); 84 freopen("pwalk.out","w",stdout); 85 int i,t1,t2,t3; 86 memset(first,-1,sizeof(first)); 87 scanf("%d%d",&n,&q); 88 for(i=1;i<n;i++){ 89 scanf("%d%d%d",&t1,&t2,&t3); 90 e[i*2-1].to=t2;e[i*2-1].w=t3;e[i*2-1].next=first[t1];first[t1]=i*2-1; 91 e[i*2].to=t1;e[i*2].w=t3;e[i*2].next=first[t2];first[t2]=i*2; 92 } 93 memset(vis,0,sizeof(vis)); 94 dfs1(1,1,0); 95 memset(vis,0,sizeof(vis)); 96 dfs2(1,1,1); 97 for(i=1;i<=times;i++) build(1,nb[i],1,i); 98 for(i=1;i<=q;i++){ 99 scanf("%d%d",&t1,&t2); 100 printf("%d ",query(t1,t2)); 101 } 102 }
T2
题意:有一个n*m的池子,每个格子有四种状态:0水面,1石头,2荷叶,3荷叶并且是出发点,4荷叶并且是目的地
每次能跳一个“日”字,问在放最少的荷叶且的情况下有多少种放置方法
思路:bfs,对于每个点标记f[i][j]为最少放置的荷叶数,num[i][j]为总步数,然后bfs,根据先 f 再更新um 的方法。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<cmath> 7 #include<queue> 8 #define inf 0x7fffffff 9 #define ll long long 10 using namespace std; 11 inline int read() 12 { 13 int x=0,f=1;char ch=getchar(); 14 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 15 while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 16 return x*f; 17 } 18 struct node{ 19 int add,num,in; 20 }a[50][50]; 21 const int dx[9]={0,1,1,-1,-1,2,2,-2,-2},dy[9]={0,2,-2,2,-2,1,-1,1,-1}; 22 int n,m,opx,opy,edx,edy; 23 bool vis[50][50]; 24 void bfs(){ 25 queue<pair<int,int> >q;vis[opx][opy]=1; 26 pair<int,int>t; 27 t.first=opx;t.second=opy; 28 q.push(t); 29 int x,y,tx,ty,i,tmp; 30 while(!q.empty()){ 31 t=q.front();q.pop(); 32 x=t.first;y=t.second;vis[x][y]=0; 33 // cout<<"bfs in "<<x<<ends<<y<<endl; 34 for(i=1;i<=8;i++){ 35 tx=x+dx[i];ty=y+dy[i]; 36 if(tx>0&&tx<=n&&ty>0&&ty<=m&&a[tx][ty].in!=2){ 37 tmp=(a[tx][ty].in==0); 38 t.first=tx;t.second=ty; 39 if(a[x][y].add+tmp<a[tx][ty].add){ 40 // cout<<" type 1: going to "<<tx<<ends<<ty<<endl; 41 a[tx][ty].add=a[x][y].add+tmp; 42 a[tx][ty].num=a[x][y].num; 43 if(!vis[tx][ty]){ 44 q.push(t);vis[tx][ty]=1; 45 } 46 continue; 47 } 48 if(a[x][y].add+tmp==a[tx][ty].add){ 49 // cout<<" type 2: going to "<<tx<<ends<<ty<<endl; 50 a[tx][ty].num+=a[x][y].num; 51 if(!vis[tx][ty]){ 52 q.push(t);vis[tx][ty]=1; 53 } 54 continue; 55 } 56 } 57 } 58 } 59 } 60 int main(){ 61 int i,j; 62 n=read();m=read(); 63 for(i=1;i<=n;i++){ 64 for(j=1;j<=m;j++){ 65 a[i][j].in=read(); 66 a[i][j].add=inf; 67 if(a[i][j].in==3){ 68 opx=i;opy=j; 69 a[i][j].add=0;a[i][j].num=1; 70 } 71 if(a[i][j].in==4){ 72 edx=i;edy=j; 73 } 74 } 75 } 76 bfs(); 77 if(a[edx][edy].add==inf) printf("-1"); 78 else printf("%d %d",a[edx][edy].add,a[edx][edy].num); 79 } 80 /* 81 82 4 5 83 1 0 0 0 0 84 3 0 0 0 0 85 0 0 2 0 0 86 0 0 0 4 0 87 88 */
T3:
题意:有n个房间,q组询问,对于每一组询问,可能是入驻连续x个人,如果没有连续x个空房就跳过,也有可能是退一批房间(退掉的有可能是空的房间)
一开始全是空房,对于每一个合理的入驻请求,输出连续x个房间中编号最小的那一个
题解:看到连续房间和询问立马想到了线段树......然而比赛的时候似乎时间不够写挂了
正解是用left,right和maxn标记每一个区间的左起连续空房、右起连续空房和最大空房数,每一次维护lazy的时候回溯处理加一步更新就好了
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 struct node{ 7 int l,r,x,y,len,lazy; 8 node(){ 9 l=r=lazy=len=0; 10 } 11 }a[200100]; 12 void build(int l,int r,int num){ 13 a[num].l=l;a[num].r=r; 14 a[num].x=a[num].y=a[num].len=r-l+1; 15 int mid=(l+r)>>1; 16 if(l!=r){ 17 build(l,mid,num<<1);build(mid+1,r,(num<<1)+1); 18 } 19 } 20 void clear(int num){ 21 if(a[num].l==a[num].r||!a[num].lazy) return; 22 int son=num<<1; 23 if(a[num].lazy==2){ 24 a[son].x=a[son].y=a[son].len=a[son].r-a[son].l+1; 25 a[son+1].x=a[son+1].y=a[son+1].len=a[son+1].r-a[son+1].l+1; 26 a[son].lazy=a[son+1].lazy=1; 27 } 28 else{ 29 a[son].x=a[son].y=a[son].len=0; 30 a[son+1].x=a[son+1].y=a[son+1].len=0; 31 a[son].lazy=a[son+1].lazy=2; 32 } 33 a[num].lazy=0; 34 } 35 int find(int len,int num){ 36 // cout<<"find "<<len<<ends<<num<<endl; 37 int son=num<<1; 38 clear(num);clear(son);clear(son+1); 39 if(a[num].x>=len) return a[num].l; 40 if(a[son].len>=len) return find(len,son); 41 if(a[son].y+a[son+1].x>=len) return a[son].r-a[son].y+1; 42 return find(len,son+1); 43 } 44 void update(int num){ 45 // cout<<"update "<<num<<endl; 46 int son=num<<1; 47 clear(son);clear(son+1); 48 a[num].x=((a[son].len==a[son].r-a[son].l+1)?a[son].x+a[son+1].x:a[son].x); 49 a[num].y=((a[son+1].len==a[son+1].r-a[son+1].l+1)?a[son+1].y+a[son].y:a[son+1].y); 50 // cout<<a[num].len<<endl; 51 a[num].len=max(a[son].len,a[son+1].len); 52 // cout<<a[num].len<<endl; 53 a[num].len=max(a[num].len,a[son].y+a[son+1].x); 54 // cout<<a[num].len<<endl; 55 } 56 void checkin(int l,int r,int num){ 57 clear(num); 58 if(a[num].l>r||a[num].r<l) return; 59 if(a[num].l>=l&&a[num].r<=r){ 60 a[num].x=a[num].y=a[num].len=0;a[num].lazy=1;return; 61 } 62 int mid=(a[num].l+a[num].r)>>1,son=num<<1; 63 if(mid>=l) checkin(l,r,son); 64 if(mid<r) checkin(l,r,son+1); 65 update(num); 66 } 67 void checkout(int l,int r,int num){ 68 clear(num); 69 if(a[num].l>r||a[num].r<l) return; 70 if(a[num].l>=l&&a[num].r<=r){ 71 a[num].x=a[num].y=a[num].len=a[num].r-a[num].l+1;a[num].lazy=2;return; 72 } 73 int mid=(a[num].l+a[num].r)>>1,son=num<<1; 74 if(mid>=l) checkout(l,r,son); 75 if(mid<r) checkout(l,r,son+1); 76 update(num); 77 } 78 int n,m,ans; 79 int main(){ 80 int i,t1,t2,t3; 81 scanf("%d%d",&n,&m); 82 build(1,n,1); 83 for(i=1;i<=m;i++){ 84 scanf("%d",&t1); 85 if(t1==1){ 86 scanf("%d",&t2); 87 if(a[1].len<t2){ 88 printf("0 ");continue; 89 } 90 ans=find(t2,1); 91 printf("%d ",ans); 92 checkin(ans,ans+t2-1,1); 93 } 94 else{ 95 scanf("%d%d",&t2,&t3); 96 checkout(t2,t2+t3-1,1); 97 } 98 } 99 }
T4:
题意:有一个图求最小生成树,使得从某一个点出发,对于路上每经过一次的点加一次该点权值,通过的边也加一次权值,问怎样最小,输出最小值
思路:kruskal,因为对于任何一个点来说,他有多少条出边就会加多少次该点的权值。
因此输入的时候把每条边加上两端端点的权值,跑kruskal,最后再加上一个点权最小的点就好了(因为根据题意这个点多跑一遍)
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #define ll long long 5 using namespace std; 6 struct erickin{ 7 int to,next,from,w; 8 }a[200100]; 9 int n,m,wei[10010],first[10010]; 10 ll ans=0; 11 int f[10010]; 12 bool cmp(erickin l,erickin r){ 13 return l.w<r.w; 14 } 15 int find(int k){ 16 return ((f[k]==k)?k:f[k]=find(f[k])); 17 } 18 void kruskal(){ 19 int i,times=0,fx,fy; 20 for(i=1;i<=n;i++) f[i]=i; 21 for(i=1;i<=m;i++){ 22 fx=find(a[i].from);fy=find(a[i].to); 23 if(fx!=fy){ 24 f[fx]=fy;ans+=a[i].w; 25 times++; 26 if(times==n-1) break; 27 } 28 } 29 } 30 int main(){ 31 int i,t1,t2,t3; 32 scanf("%d%d",&n,&m); 33 for(i=1;i<=n;i++) scanf("%d",&wei[i]); 34 for(i=1;i<=m;i++){ 35 scanf("%d%d%d",&t1,&t2,&t3); 36 a[i].to=t2;a[i].from=t1;a[i].next=first[t1];first[t1]=i;a[i].w=t3*2+wei[t1]+wei[t2]; 37 } 38 // for(i=1;i<=m;i++) cout<<a[i].from<<ends<<a[i].to<<ends<<a[i].w<<endl; 39 sort(a+1,a+m+1,cmp); 40 sort(wei+1,wei+n+1); 41 kruskal(); 42 printf("%lld",ans+wei[1]); 43 } 44 /* 45 5 7 46 10 47 10 48 20 49 6 50 30 51 1 2 5 52 2 3 5 53 2 4 12 54 3 4 17 55 2 5 15 56 3 5 6 57 4 5 12 58 */