Codeforces Round #503(div2) 做题记录
前几天有点咕,马上题解会跟上~
A.
题意:
有n个楼,每个楼有h层,相邻两个楼在(a,b)之间有通道
k次询问,每次问(tA,fA)到(tB,fB)(t为楼的编号,f为楼层)的最短路
题解:
如果不在(a,b)层之间那先爬到离他最近的(a,b)层之间的楼层
然后通过通道直接走,先走到tB走到对应楼层
注意tA=tB时特判
1 #include<bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 int n,h,a,b,q; 5 int main() 6 { 7 scanf("%d%d%d%d%d",&n,&h,&a,&b,&q); 8 while(q--) 9 { 10 int t1,f1,t2,f2; 11 int ans=0; 12 scanf("%d%d%d%d",&t1,&f1,&t2,&f2); 13 if(t1==t2) 14 { 15 printf("%d\n",abs(f2-f1)); 16 continue; 17 } 18 ans+=abs(t2-t1); 19 int now=f1; 20 if(f1<a)ans+=abs(f1-a),now=a; 21 else if(f1>b)ans+=abs(f1-b),now=b; 22 ans+=abs(now-f2); 23 printf("%d\n",ans); 24 } 25 return 0; 26 }
B.
题意:
给你一个n个点的基环内向树,询问从每个点出发第一个走到的到过一次的点是什么
题解:
n很小,暴力模拟
1 #include<bits/stdc++.h> 2 #define maxn 1005 3 using namespace std; 4 int n; 5 int p[maxn]; 6 bool vis[maxn]; 7 int main() 8 { 9 scanf("%d",&n); 10 for(int i=1;i<=n;++i)scanf("%d",&p[i]); 11 for(int i=1;i<=n;++i) 12 { 13 int u=i; 14 memset(vis,0,sizeof(vis)); 15 while(!vis[u])vis[u]=1,u=p[u]; 16 printf("%d ",u); 17 } 18 return 0; 19 }
C.
题意:
有n个选民,m个党派
你要给一些选民钱,让他们转投1号党派的票
每个选民有一个ci和一个pi,表示让他转投的钱数和他原本投的党派
然后要让1号党派获胜,最少要用多少钱
题解:
枚举其他党派的人数上限
然后把其他党派中人数超过这个上限的强行加到1号中(当然是贪心找最小的加)
如果不超过的另外开个数组
最后sort一下,选最小的几个
1 #include<bits/stdc++.h> 2 #define ll long long 3 #define maxn 3005 4 using namespace std; 5 int n,m; 6 int p[maxn],c[maxn]; 7 vector<int> val[maxn]; 8 int num[maxn]; 9 int a[maxn],cnt; 10 int main() 11 { 12 scanf("%d%d",&n,&m); 13 for(int i=1;i<=n;++i) 14 { 15 scanf("%d%d",&p[i],&c[i]); 16 val[p[i]].push_back(c[i]); 17 } 18 for(int i=1;i<=m;++i)sort(val[i].begin(),val[i].end()); 19 ll ans=1e15; 20 for(int i=n;i>=0;--i) 21 { 22 ll res=0,tot=val[1].size(); 23 cnt=0; 24 for(int j=2;j<=m;++j)if(val[j].size()>i) 25 { 26 for(int k=0;k<val[j].size()-i;++k) 27 { 28 res+=val[j][k];++tot; 29 } 30 } 31 for(int j=2;j<=m;++j) 32 { 33 int mind=val[j].size()-i; 34 for(int k=max(mind,0);k<val[j].size();++k) 35 { 36 a[++cnt]=val[j][k]; 37 } 38 } 39 sort(a+1,a+cnt+1); 40 for(int j=1;j<=cnt;++j) 41 { 42 if(tot>i)break; 43 tot++;res+=a[j]; 44 } 45 ans=min(ans,res); 46 } 47 cout<<ans<<endl; 48 return 0; 49 }
D.
题意:
交互题
给你一个环,每个值和左右数值的差值是±1
然后你不知道每个点的值
你可以询问每个点的值,求a[i]=a[i+n/2]的位置
题解:
考虑60次询问,大概logn级别
然后令f(x)=a[x]-a[x+n/2]
这个东西在x∈[1,n/2]范围内单调
二分一下就好了
(比赛的时候边界写挂了导致fst QAQ)
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n; 4 int ask(int pos) 5 { 6 int x,y; 7 printf("? %d\n",pos); 8 fflush(stdout); 9 scanf("%d",&x); 10 printf("? %d\n",pos+n/2); 11 fflush(stdout); 12 scanf("%d",&y); 13 return x-y; 14 } 15 int main() 16 { 17 scanf("%d",&n); 18 if(n&1) 19 { 20 printf("! -1\n"); 21 return 0; 22 } 23 int l=1,r=n/2; 24 int fl=ask(l),fr=ask(r); 25 if(fl==0) 26 { 27 printf("! %d\n",l); 28 return 0; 29 } 30 if(fr==0) 31 { 32 printf("! %d\n",r); 33 return 0; 34 } 35 if(fl>0&&fr<0) 36 { 37 while(l<=r) 38 { 39 int mid=(l+r)>>1; 40 int t=ask(mid); 41 if(t==0) 42 { 43 printf("! %d\n",mid); 44 return 0; 45 } 46 if(t>0)l=mid+1;else r=mid-1; 47 } 48 } 49 if(fl<0&&fr>0) 50 { 51 while(l<=r) 52 { 53 int mid=(l+r)>>1; 54 int t=ask(mid); 55 if(t==0) 56 { 57 printf("! %d\n",mid); 58 return 0; 59 } 60 if(t>0)r=mid-1;else l=mid+1; 61 } 62 } 63 puts("! -1"); 64 return 0; 65 }
E.
题意:
给你一个有向图(无自环),你需要构造一个点集Q,满足:
x∈Q,对任意边(x,y)或(y,x),y∉Q
x∉Q,dist(x,Q)<=2(x到Q内任意一点的距离)
题解:
先从前向后遍历一遍
对于一个没有访问过的点u(vis[u]==0),把从u走一步能到达的点v标记上vis[v]=1,并把u暂时加入点集(used[u]==0)
然后考虑反向遍历一遍,如果used[u]==1,那么把u走一步能到的点v踢出点集
1 #include<bits/stdc++.h> 2 #define maxn 1000005 3 using namespace std; 4 int n,m; 5 vector<int> g[maxn],gg[maxn]; 6 bool vis[maxn],used[maxn]; 7 int ans[maxn]; 8 int main() 9 { 10 scanf("%d%d",&n,&m); 11 for(int u,v,i=1;i<=m;++i) 12 { 13 scanf("%d%d",&u,&v); 14 g[u].push_back(v); 15 gg[v].push_back(u); 16 } 17 for(int u=1;u<=n;++u)if(!vis[u]) 18 { 19 used[u]=1; 20 for(int j=0;j<g[u].size();++j)vis[g[u][j]]=1; 21 } 22 int cnt=0; 23 for(int u=n;u;--u)if(used[u]) 24 { 25 ans[++cnt]=u; 26 for(int j=0;j<g[u].size();++j)used[g[u][j]]=0; 27 } 28 printf("%d\n",cnt); 29 for(int i=1;i<=cnt;++i)printf("%d ",ans[i]); 30 return 0; 31 }