(CF1574X)Codeforces Round #731 (Div. 3)
A.Shortest Path with Obstacle
题意:坐标系中有三个点A,B,F,问A到达B且不经过F的最短路径。
直接分两种情况,如果ABF在一条直线上则需要绕道,即A到B的直线距离加上2即可,若不在一条直线,则直接输出A到B的折线距离。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--)
{
vector<int>a(2);
vector<int>b(2);
vector<int>c(2);
cin>>a[0]>>a[1];
cin>>b[0]>>b[1];
cin>>f[0]>>f[1];
int ans=abs(a[0]-b[0])+abs(a[1]-b[1]);
if((a[0]==b[0]&&a[0]==f[0]&&min(a[1],b[1])<f[1]&&f[1]<max(a[1],b[1]))
||(a[1]==b[1]&&a[1]==f[1]&&min(a[0],b[0])<f[0]&&f[0]<max(a[0],b[0])))
ans+=2;
cout<<ans<<'\n';
}
}
B.Alphabetical Strings
题意:判断一个字符串是否可以按字母表的顺序依次排列出来,比如ihfcbadeg,首先给出a,然后b可以选择放在最左端或最右端,接着再看c能不能放在最左端或最右端,依此类推。
故可以发现满足条件的字符串,必然满足下面两点:
a左边必然严格下降,a右边必然严格上升。
出现的字母个数为n,且为字母表中第一个到第n个字符。
#include<bits/stdc++.h>
using namespace std;
string s;
int main()
{
int t;
cin>>t;
while(t--)
{
int index,flag=0;
cin>>s;
for(int i=0;i<s.size();i++)
{
if(s[i]=='a')index=i;
if(s.find(char('a'+i))!=string::npos)
continue;
else
{
flag=1;
break;
}
}
if(!flag)
for(int i=1;i<=index;i++)
{
if(s[i]>s[i-1])
{
flag=1;
break;
}
}
if(!flag)
for(int i=index+1;i<s.size();i++)
{
if(s[i]<s[i-1])
{
flag=1;
break;
}
}
if(!flag)cout<<"yes"<<'\n';
else cout<<"no"<<'\n';
}
}
C.Pair Programming
题意:给出一个初始值k,以及两个size一样的数组,每次取两个数组的第一个数中的一个,且要满足取出的数不大于k,若取出的数为0则还会让k加一。
故直接贪心即可。
#include<bits/stdc++.h>
using namespace std;
int k,n,m;
int main()
{
int t;
cin>>t;
while(t--)
{
cin>>k>>n>>m;
vector<int>a(n+1);
vector<int>b(m+1);
vector<int>c(n+m+1);
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=m;i++)
cin>>b[i];
int i=1,j=1,flag=0,tot=0;
while(i<=n||j<=m)
{
if(a[i]<=k&&i<=n)
{
c[++tot]=a[i];
if(a[i]==0)k++;
i++;
}
else if(b[j]<=k&&j<=m)
{
c[++tot]=b[j];
if(b[j]==0)k++;
j++;
}
else
{
flag=1;
break;
}
}
if(flag)cout<<-1<<'\n';
else
{
for(int i=1;i<n+m;i++)
cout<<c[i]<<' ';
cout<<c[n+m]<<'\n';
}
}
}
D.Co-growing Sequence
题意:定义grow数组为其中任意一个数与它的下一个数做"&"运算后值不变。给出一个数组x,将每一个xi与一个数yi做异或运算后形成的新数组为grow数组,求最小字典序的y数组。
首先二进制预处理数组中的每一个数。然后将每一个数xi与前一个数xi-1按位比较,若前一个数的当前位为0,前一个数当前位为1,则相应的yi的当前位必为1。复杂度o(30n)。
#include<bits/stdc++.h>
using namespace std;
int n;
int main()
{
int t;
cin>>t;
while(t--)
{
cin>>n;
vector<int>a(n+1);
vector<vector<int>>b(n+1,vector<int>(33));
for(int i=1;i<=n;i++)
for(int j=30;j>=0;j--)
b[i][j]=0;
for(int i = 1; i <= n; ++ i)
{
cin>>a[i];
int j=0;
while(a[i])
{
b[i][j]=a[i]% 2;
a[i]/=2;
j++;
}
}
cout<<0<<' ';
for(int i=2;i<=n;i++)
{
int res=0;
for(int j=30;j>=0;j--)
{
if(b[i-1][j]==1&&b[i][j]==0)
{
b[i][j] = 1;
res += (1 << j);
}
}
cout<<res<<' ';
}
cout<<'\n';
}
}
E.Air Conditioners
题意:给出n个房间,并有k台空调位于k个房间,每台空调有一个冷气值,分别求这n个房间到k个空调房的(距离值+冷气值)的最小值。
对于每一个不为空调房的房间,它到它前面所有的空调房的距离加冷气的最小值为它前一个房间的最小值加一。同理,它到它后面所有的空调房的最小值为它后面一个房间的最小值加一,最后取再取一次min。
对于每一个为空调房的房间,只用在刚刚的基础上,再对自身的冷气值取一次min。
#include<bits/stdc++.h>
using namespace std;
int k,n,m;
int main()
{
int t;
cin>>t;
while(t--)
{
cin>>n>>k;
vector<int>a(n+2);
vector<int>vis(n+2);
vector<int>r(n+2);
vector<int>l(n+2);
vector<int>s(n+2);
vector<int>q(n+2);
for(int i=1;i<=k;i++)
cin>>a[i];
for(int i=1;i<=n;i++)q[i]=2e9;
for(int i=1;i<=k;i++)
cin>>q[a[i]];
l[0]=2e9,r[n+1]=2e9;
for(int i=1;i<=n;i++)
{
l[i]=min(l[i-1]+1,q[i]);
r[n-i+1]=min(r[n-i+2]+1,q[n-i+1]);
}
for(int i=1;i<=n;i++)
{
s[i]=min(l[i],r[i]);
cout<<s[i]<<' ';
}
}
}
F.Array Stabilization (GCD version)
题意:给出一个数组a,将每一个数变成其与后一个数的gcd,形成一个新的数组,问最少多少次可以让数组a中每一个数相等。
可以发现,第i次形成的数组即数组a中每一个数与其后i个数做一次gcd所形成的。故可以用线段树来维护区间的gcd值。然后求次数则用二分答案,因为最多n次操作数组中每个数必相等。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define maxn 400005
ll nzk[maxn*4];
ll a[maxn];
int n;
void build(int root,int l,int r)
{
if(l==r)
{
nzk[root]=a[l];
return;
}
int mid=l+(r-l)/2;
build(root*2,l,mid);
build(root*2+1,mid+1,r);
nzk[root]=__gcd(nzk[root*2],nzk[root*2+1]);
}
ll query(int root,int l,int r,int ls,int rs)
{
if(ls<=l&&rs>=r)
return nzk[root];
int mid=l+(r-l)/2;
if(ls>mid)
return query(root*2+1,mid+1,r,ls,rs);
else if(rs<=mid)
return query(root*2,l,mid,ls,rs);
else
return __gcd(query(root*2+1,mid+1,r,ls,rs),query(root*2,l,mid,ls,rs));
}
bool check(int k)
{
for(int i=2;i<=n;i++)
{
ll a=query(1,1,2*n,i,i+k);
ll b=query(1,1,2*n,i-1,i+k-1);
if(a==b)
continue;
else
return false;
}
return true;
}
int main()
{
int t;
cin>>t;
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
a[i+n]=a[i];
}
build(1,1,2*n);
int l=0,r=n-1;
while(l<r)
{
int mid=l+(r-l)/2;
if(check(mid))r=mid;
else l=mid+1;
}
cout<<l<<'\n';
}
}
G.How Many Paths?
题意:给出n个点m条边的有向图,对于第i个点,若从1到i没有路径,输出0,有一条路径,输出1,有多条路径,输出2,无数条则输出n。
DFS的搜索中结点状态有3种:未被搜索到,正在扩展搜索,以及搜索完毕。根据DFS的特性,如果正在扩展的结点被再次访问,则说明存在环;如果以及搜索完毕的结点被再次访问,则说明有多条路径。
在环中的结点可以到达的点,一定是无限路径;多条路径的结点可以到达的点也一定是多条路径。
#include<bits/stdc++.h>
using namespace std;
#define maxn 400005
int n,t,m;
int color[maxn];
int c[2][maxn];
vector<vector<int>>e;
vector<int>group[2];
void dfs1(int u)
{
color[u]=1;
for(auto v:e[u])
{
if(color[v]==0)
dfs1(v);
else
group[color[v]-1].push_back(v);
}
color[u]=2;
}
void dfs2(int u,int i)
{
c[i][u]=1;
for(auto v:e[u])
{
if(c[i][v]==0)
dfs2(v,i);
}
c[i][u]=2;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
e=vector<vector<int>>(n+1);
memset(color,0,sizeof(color));
memset(c,0,sizeof(c));
group[0].clear();
group[1].clear();
for(int i=0;i<m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
e[a].push_back(b);
}
dfs1(1);
for(int i=0;i<2;i++)
for(auto u:group[i])
dfs2(u,i);
for(int i=1;i<=n;i++)
{
int ans=0;
if(color[i])
{
ans=1;
if(c[0][i])
ans=-1;
else if(c[1][i])
ans=2;
}
printf("%d%c",ans,i==n?'\n':' ');
}
}
}
总结
由于是div3,比赛题目还是不太难的,做出六题应该问题不大,但第四题卡了很久,最后思路和标答一样,却因为数组清零操作t了,吸取经验继续努力。