2020 GDUT Rating Contest III (Div2)
A Wormhole Sort
题意:
给出N个打乱顺序的数,和M条边(a,b,c)表示在a位置的数可以和在b位置的数交换,这条边的大小是c,问要使这组数恢复升序需要经过的大小最小的最大的边是多大。
解题思路:
直接二分答案,通过把大小大于等于答案的边用来连成一个连通块,看乱序的点是否在同一个连通块来判断该答案是否合法。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
struct node{
int from,to,size;
}edge[100005];
int cow[100005],f[100005];
int n;
bool cmp(node a,node b){
return a.size>b.size;
}
int father(int u){
return f[u]==u?u:f[u]=father(f[u]);
}
void merge(int x,int y){
int fx=father(x),fy=father(y);
f[fx]=f[fy];
return;
}
bool check(int x){
for (int i=1;i<=n;i++)
f[i]=i;
for (int i=1;i<=x;i++)
merge(edge[i].from,edge[i].to);
for (int i=1;i<=n;i++)
if (father(i)!=father(cow[i])) return false;
return true;
}
int main(){
int m;
scanf("%d %d",&n,&m);
bool flg=true;
for (int i=1;i<=n;i++){
scanf("%d",&cow[i]);
if (cow[i]!=i) flg=false;
}
for (int i=1;i<=m;i++)
scanf("%d %d %d",&edge[i].from,&edge[i].to,&edge[i].size);
if (flg)
{
printf("-1\n");
return 0;
}
sort(edge+1,edge+m+1,cmp);
int l=0,r=m;
while (r-l>1){
int mid=(l+r)/2;
if (check(mid)) r=mid;
else l=mid;
}
printf("%d\n",edge[r].size);
return 0;
}
B Loan Repayment
题意:
John欠了N加仑牛奶需要在K天内还清,他每天会最少还M加仑牛奶。在已经还了G加仑奶之后,对于一个正整数X,John会每天还max((N-G)/X,M)加仑奶。
求X的最大值。
1<=N,M,K<=1e12
解题思路:
二分答案,如果直接模拟的话会出现连续很多天给Y加仑奶的情况导致超时,所以只要用数学优化一下模拟的过程,让每次换奶的时候Y的值都会发生变化即可。
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<iostream>
using namespace std;
long long n,k,m;
bool check(long long x){
long long cnt=1LL*k*m;
long long g=0,d;
long long y=n/x;
int t=0;
while (y>m&&t<=k)
{
if (ceil(1.0*(n-y*x-g)/y)+t<=k) d=ceil(1.0*(n-y*x-g)/y+0.000000000000001);
else d=k-t+1;
t+=d;
g+=y*d;
cnt+=(y-m)*d;
if (cnt>=n) return true;
y=(n-g)/x;
}
return false;
}
int main(){
scanf("%lld %lld %lld",&n,&k,&m);
long long l=1,r=1e12+1;
while (r-l>1){
long long mid=(l+r)/2;
if (check(mid)) l=mid;
else r=mid;
}
printf("%lld\n",l);
return 0;
}
D Race
题意:
Bessie要跑K米,她每个时刻可以加速1或者减速1或者匀速跑,她结束跑步时速度不能高于X,现在给出N个X,问每个X对应需要的结束跑步的时间。
解题思路:
二分找最大速度,然后模拟一下算出时间即可。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int q,len,n,k;
bool check(long long x)
{
long long a=1LL*(1+x)*x/2;
long long b;
if (x<q) b=0;
else b=(q+x-1)*(x-q)/2;
len=a+b;
if (a+b<=k) return true;
else return false;
}
int main(){
scanf("%d %d",&k,&n);
int ans;
for (int i=1;i<=n;i++){
scanf("%d",&q);
int l=1,r=1000000;
while (l<=r)
{
int mid=(l+r)/2;
if (check(mid))
{
ans=mid;
l=mid+1;
}
else r=mid-1;
}
bool x=check(ans);
long long t=ans;
if (ans>=q) t+=ans-q;
long long last=k-len;
if (last!=0)
{
t+=last/ans;
if (last%ans!=0) t++;
}
printf("%lld\n",t);
}
}
E.Word Processor
题意:
给出一个有N个单词的句子。要求拆成每行不超过K个字母输出(不能拆开完整的单词)。
解题思路:
直接判每个单词长度,如果符合要求就输出。
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<iostream>
using namespace std;
int main(){
int n,k;
string s[105];
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++)
cin>>s[i];
for (int i=1;i<=n;){
int cnt=s[i].size();
int j=i+1;
while (cnt+s[j].size()<=k&&j<=n) {cnt+=s[j].size();j++;}
for (int k=i;k<j;k++)
cout<<s[k]<<" ";
printf("\n");
i=j;
}
}
H:Photoshoot
题意:
给长为N-1个数列a,求无相同元素的数列B使得对于1到n-1都有b[i]+b[i+1]=a[i],且对于所有的b[i]有1<=b[i]<=N。输出字典序最小的方案
解题思路:
直接从1开始枚举b[1]即可,找到第一种符合的方案就是字典序最小的方案。
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<iostream>
using namespace std;
int n;
int a[1005],b[1005];
bool vis[1005];
bool check(int x){
memset(vis,false,sizeof(vis));
a[1]=x;vis[x]=true;
for (int i=1;i<n;i++){
if (b[i]-a[i]<=0||b[i]-a[i]>n) return false;
if (vis[b[i]-a[i]]) return false;
a[i+1]=b[i]-a[i];
vis[b[i]-a[i]]=true;
}
return true;
}
int main(){
scanf("%d",&n);
for (int i=1;i<n;i++)
scanf("%d",&b[i]);
for (int i=1;i<b[1];i++)
if (check(i))
{
for (int j=1;j<=n;j++)
printf("%d ",a[j]);
return 0;
}
}