Codeforces Round #731 (Div. 3)解题报告
最近开始训练思维了,先从cf的div3练起吧,EFG都是后面补出来的,还是很有收获的,记录下每个题的做法
A Shortest Path with Obstacle
很基础,就判断下中间点有没有被两个点夹住即可。
#include<bits/stdc++.h>
using namespace std;
int xx1,x2,x3,yy1,y2,y3;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int ans=0;
scanf("%d%d",&xx1,&yy1);
scanf("%d%d",&x2,&y2);
scanf("%d%d",&x3,&y3);
if(xx1==x2&&xx1==x3&&((y3>yy1&&y3<y2)||(y3>y2&&y3<yy1)))
{
ans=abs(y2-yy1)+2;
}
else if(yy1==y2&&yy1==y3&&((x3>xx1&&x3<x2)||(x3>x2&&x3<xx1)))
{
ans=abs(x2-xx1)+2;
}
else {
ans=abs(xx1-x2)+abs(yy1-y2);
}
printf("%d\n",ans);
}
return 0;
}
B - Alphabetical Strings
这题目的是让你来回前后插入字母,问是否可以满足条件,思想类似尺取,先找到字母a同时维护l和r,模拟整个字符串出现的过程即可
#include<bits/stdc++.h>
using namespace std;
int t;
string s;
int main()
{
cin>>t;
while(t--)
{
cin>>s;
int len=s.size();
int a,ff=0;
for(int i=0;i<len;i++)
{
if(s[i]=='a')
{
a=i;
ff=1;
break;
}
}
int flag=0;
if(ff==0)flag=1;
int l=a,r=a;
char c='a';
len--;
while(len--)
{
if(s[l-1]==c+1)
{
l--;
}
else if(s[r+1]==c+1)
{
r++;
}
else {
flag=1;
break;
}
c++;
}
if(flag==0)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
}
C - Pair Programming
题目就是要把两个数组拼起来,尽可能满足所有点都可以被解决,如果不行就-1,直接模拟,如果第一个不行就第二个,再不行就说明不存在解
#include<bits/stdc++.h>
using namespace std;
int a[500],b[500],ans[500],k,t,n,m;
int main()
{
scanf("%d",&t);
while(t--)
{
memset(ans,0,sizeof(ans));
scanf("%d%d%d",&k,&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int i=1;i<=m;i++)
{
scanf("%d",&b[i]);
}
int w=n+m;
int l=1,r=1;
int cnt=1;
int flag=0;
while(w--)
{
if(a[l]<=k&&l<=n)
{
ans[cnt++]=a[l];
l++;
if(a[l-1]==0)k++;
}
else if(b[r]<=k&&r<=m)
{
ans[cnt++]=b[r];
r++;
if(b[r-1]==0)k++;
}
else {
flag=1;
break;
}
}
if(flag==1)
{
printf("-1\n");
}
else {
for(int i=1;i<cnt;i++)
{
printf("%d ",ans[i]);
}
printf("\n");
}
}
return 0;
}
D. -Co-growing Sequence
是一个有趣的位运算题目,不难发现根据题目给出的条件公式我们只需要确定b【0】就能得到后续的所有解定制,为了保证字典序最小,b[0]一定是0.之后就可以根据定值得到后续所有b数组的值。实现可以自己设计一种满足要求的位运算,或者直接按位枚举解决每个每个位的值
#include<bits/stdc++.h>
using namespace std;
int a[200010],ans[200010],c[200010],t,n;
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
ans[i]=(c[i-1]|a[i])^a[i];
c[i]=a[i]^ans[i];
}
for(int i=1;i<=n;i++)
printf("%d ",ans[i]);
printf("\n");
}
return 0;
}
E. Air Conditioners
这是很有趣的一道题,给你一些空调的位置,告诉你空调的影响随着距离逐渐减弱,问所有位置的最小温度。我们先假设所有位置的温度都是无穷大。然后每个位置的温度只可能由他的左边或右边决定。所以我们正反各扫一遍,做dp。其实就是从头开始,逐渐更新影响。因为所有空调在延申过程中的损耗一样,所以在过程中维护当前对环境影响最有效的空调即可。在本题中之间用一个数组表示状态即可。需要注意在枚举过程中由于要用到上一个点的状态。要注意边界问题。
#include<bits/stdc++.h>
using namespace std;
int q,n,k;
int a[300010],t[300010],f[300010];
int main()
{
scanf("%d",&q);
while(q--)
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
f[i]=10000000000;
for(int i=1;i<=k;i++)
scanf("%d",&a[i]);
for(int i=1;i<=k;i++)
scanf("%d",&t[i]),f[a[i]]=t[i];
for(int i=2;i<=n;i++)
{
f[i]=min(f[i],f[i-1]+1);
}
for(int i=n-1;i>=1;i--)
f[i]=min(f[i],f[i+1]+1);
for(int i=1;i<=n;i++)
printf("%d ",f[i]);
printf("\n");
}
return 0;
}
F - Array Stabilization (GCD version)
也是一道很有趣的题,关键性质在于,数组最多进行n-1轮操作就全部相等,并且值等于全部gcd起来的值,原因是其实每次多做一轮就是让当前位可以和后面更多的一部分一起gcd。那么这个问题可以被分解成两部分,一部分是区间gcd,另一部分是一个有关单调性的可行性问题,也就是二分答案问题。而区间gcd可以用st表和线段树维护。这里两种都可以。由于没有修改操作。st表好写一点。常数也小。复杂度是O(Nlog(N)log(N))第二个log是在预处理st表时需要用gcd的操作。容易忽略掉。但是这题给了4秒肯定是绰绰有余的。所以st表预处理好区间gcd之后。我们可以二分答案确定最小轮数。
关于st表维护gcd。他是因为。重复区域gcd不影响最终结果。等同于最大值和最小值。然后本题是成环的。可以用一个小技巧,就是数组开到2*n,在n之后在连接一段值。其他的就是比较模板的部分了
#include<bits/stdc++.h>
using namespace std;
int t,n,end1;
int a[400010],st[400010][30];//st表维护区间gcd
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
void init()
{
for(int i=1;i<=2*n;i++)
st[i][0]=a[i];
for(int i=1;(1<<i)<=n;i++)
for(int j=1;j+(1<<i)<=2*n;j++)
st[j][i]=gcd(st[j][i-1],st[j+(1<<(i-1))][i-1]);//这里是因为重叠区域在做一次gcd不影响结果,前面的意义在于控制两个区间长度都为2的幂次
}
int findgcd(int l,int r)
{
int k=log2(r-l+1);
return gcd(st[l][k],st[r-(1<<k)+1][k]);
}
int check(int o)
{
for(int i=1;i<=n;i++)
if(findgcd(i,i+o)!=end1)return 0;
return 1;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i+n]=a[i];
if(i==1)end1=a[i];
else end1=gcd(a[i],end1);
}
init();
int l=0,r=n-1;
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid))
r=mid;
else
l=mid+1;
}
printf("%d\n",l);
}
return 0;
}
G - How Many Paths?
这题本质就是一个搜索问题,遇到环之后,说明后面所有点肯定都无解。用另一个状态表示现在是否成环,如果重复访问同一个点说明成环了,注意要回溯。对于已经被标记不可能的点,说明至少已经进去搜过一次了,所以之间减枝剪掉即可。
#include<bits/stdc++.h>
using namespace std;
int t,n,m,a,b;
const int maxn=400010;
vector<int>G[maxn];
int vis[maxn],ans[maxn];
void dfs(int k,int now)
{
if(now==1)ans[k]=-1;
else if(ans[k]<2)ans[k]++;
vis[k]=1;
for(auto it:G[k])
{
if(ans[it]==-1)continue;
if(vis[it]||now)dfs(it,1);
else if(ans[it]<2)dfs(it,0);
}
vis[k]=0;
}
int main()
{
scanf("%d",&t);
while(t--)
{
memset(ans,0,sizeof(ans));
memset(vis,0,sizeof(vis));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
G[i].clear();
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
G[a].push_back(b);
}
dfs(1,0);
for(int i=1;i<=n;i++)
printf("%d ",ans[i]);
printf("\n");
}
return 0;
}