Codeforces Round #731 (Div. 3)
A. Shortest Path with Obstacle(题目)
题目大意:在一个方格中,给出\(A,B,F\)三点,每次能向上下左右移动一格,但不能经过\(F\)问\(从A到B\)最少要移动多少次
分析:
- 假设不考虑\(F\),那么\(A->B\)的最短距离就是\(|A_x-B_x|+|A_y-B_y|\)
- 之后我们发现如果\(A_x\not=B_x 或 A_y\not=B_y\)那\(A,B\)之间其实是有很多个最短路径的,其中肯定有不经过\(F\)的,那么直接输出\(|A_x-B_x|+|A_y-B_y|\)
- 但如果\(A_x=B_x 或 A_y=B_y\)呢?我们先假设\(A_x=B_x\),那么最短路径就只有一条,为线段\(AB\),如果要经过\(F\),那么\(F\)要满足在\(AB\)上,则\(F_x=A_x=B_y,min(A_y,B_y) \leqslant F_y \leqslant max(A_y,B_y)\),所以\(AB\)间的距离就要绕路,其实就是\(|A_x-B_x|+|A_y-B_y|+2\),如果\(F_y\)不满足条件,还是与\(1.\)一样。
- \(A_y=B_y同理\)
代码实现:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
void read(int &sum)
{
sum=0;char last='w',ch=getchar();
while (ch<'0' || ch>'9') last=ch,ch=getchar();
while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
if (last=='-') sum=-sum;
}
int t;
int Ax,Ay,Bx,By,Fx,Fy;
int main()
{
// freopen("M.in","r",stdin);
// freopen("M.out","w",stdout);
read(t);
while (t--)
{
read(Ax),read(Ay),read(Bx),read(By),read(Fx),read(Fy);
if ((Ax==Bx && Bx==Fx) || (Ay==By && By==Fy))
{
if ((Ax==Bx && Fy>min(Ay,By) && Fy<max(Ay,By)) || (Ay==By && Fx>min(Ax,Bx) && Fx<max(Ax,Bx)))
printf("%d\n",abs(Ax-Bx)+abs(Ay-By)+2);
else
printf("%d\n",abs(Ax-Bx)+abs(Ay-By));
}
else printf("%d\n",abs(Ax-Bx)+abs(Ay-By));
}
// fclose(stdin);fclose(stdout);
return 0;
}
B. Alphabetical Strings(题目)
题目大意:有一个字符串\(a\),第一次可以在字符串的头或尾加上一个\(b\),第二次可以在字符串的头或尾加上一个\(c\)……现在给出一个字符串\(s\)问\(s\)是不是用上述方法生成的字符串
分析:找到\(a\),从\(a\)开始\(dfs\)每次看看当前已经匹配的字符串的首尾有没有当前要寻找的字符,如果把整个\(s\)遍历完了都每问题,就是,否则不是
代码实现:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
void read(int &sum)
{
sum=0;char last='w',ch=getchar();
while (ch<'0' || ch>'9') last=ch,ch=getchar();
while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
if (last=='-') sum=-sum;
}
int t,len;
char s[30];
char ch[27]={'a','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
bool dfs(int x,int wh,int ht)
{
// printf("%d %d %d\n",x,wh,ht);
if (wh==len+1) return true;
if (ht==1)
{
if (s[x-1]==ch[wh]) return dfs(x-1,wh+1,1);
else if (s[x+wh-1-1+1]==ch[wh]) return dfs(x+wh-1-1+1,wh+1,0);
else return false;
}
else
{
if (s[x+1]==ch[wh]) return dfs(x+1,wh+1,0);
else if (s[x-wh+1+1-1]==ch[wh]) return dfs(x-wh+1+1-1,wh+1,1);
else return false;
}
}
int main()
{
// freopen("M.in","r",stdin);
// freopen("M.out","w",stdout);
read(t);
while (t--)
{
scanf("%s",s+1),len=strlen(s+1);
bool bz=false;
for (int i=1;i<=len;i++)
if (s[i]=='a')
bz=dfs(i,2,1);
if (bz==true)
printf("YES\n");
else
printf("NO\n");
}
// fclose(stdin);fclose(stdout);
return 0;
}
C. Pair Programming(题目)
题目大意:给出一个数\(k\),和两个长度分别为\(n,m\)的数列\(a,b\),每次操作可以从\(a或b\)的队首取走一个数\(a_i或b_j\)。如果,\(a_i或b_j=0\),那么将\(k+1\),否则如果\(k\geqslant _i 或 k\geqslant b_j\)就可以执行,否则就不可以执行这个操作,问能不能将\(a,b\)都取完
分析:
- 我们可以利用贪心的思想,因为要\(k\geqslant a_i或b_j\),那么我们肯定要\(k\)尽可能的大,于是就要先执行\(a,b\)中队首为\(0\)的,如果都不为\(0\),就先执行\(a_i,b_j\)中较小的
- 如果执行完了就输出操作顺序,否则输出\(-1\)
代码实现:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
void read(int &sum)
{
sum=0;char last='w',ch=getchar();
while (ch<'0' || ch>'9') last=ch,ch=getchar();
while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
if (last=='-') sum=-sum;
}
int t;
int n,m,k;
int a[110],b[110];
int main()
{
// freopen("M.in","r",stdin);
// freopen("M.out","w",stdout);
read(t);
while (t--)
{
read(k),read(n),read(m);
for (int i=1;i<=n;i++) read(a[i]),a[n+1]=1<<30;
for (int j=1;j<=m;j++) read(b[j]),b[m+1]=1<<30;
int i=1,j=1;
int ans[220],len=0;
while (1)
{
//printf("%d %d\n",i,j);
if (i>n && j>m) break;
if (a[i]==0 && i<=n) k++,ans[++len]=a[i],i++;
else if (b[j]==0 && j<=m) k++,ans[++len]=b[j],j++;
else if (a[i]<=b[j] && i<=n && a[i]<=k) ans[++len]=a[i],i++;
else if (a[i]>b[j] && j<=m && b[j]<=k) ans[++len]=b[j],j++;
else break;
}
if (len==n+m) { for (int i=1;i<=len;i++) printf("%d ",ans[i]); }
else printf("-1");
printf("\n");
}
// fclose(stdin);fclose(stdout);
return 0;
}
D. Co-growing Sequence(题目)
题目大意:给出\(A\)数列,找到一个字典序最小的\(B\)数列,令数列\(C\),\(C_i=A_i \oplus B_i\),且\(C_i \ \ AND\ \ C_{i+1}=C_i\)
分析:
- 由题目可以知道$$(A_i \oplus B_i) \ \ AND \ \ (A_{i+1} \oplus B_{i+1})=(A_i \oplus B_i)$$因为我们要\(B_{i+1}\)尽可能小,所以取\((A_{i+1} \oplus B_{i+1})\)的最小值$$\therefore (A_{i+1} \oplus B_{i+1})=(A_i \oplus B_i)$$$$\therefore B_{i+1}=A_i \oplus B_i \oplus A_{i+1}$$
- 这样\(B\)的递推式就出来了,有因为字典序要最小,所以\(B_1=0\),递推即可
代码实现:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
typedef long long ll;
void read(int &sum)
{
sum=0;char last='w',ch=getchar();
while (ch<'0' || ch>'9') last=ch,ch=getchar();
while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
if (last=='-') sum=-sum;
}
int t;
int n;
int x[2*100010];
int y[2*100010];
signed main()
{
// freopen("M.in","r",stdin);
// freopen("M.out","w",stdout);
read(t);
while (t--)
{
read(n);
for (int i=1;i<=n;i++)
read(x[i]);
y[1]=0;
for (int i=2;i<=n;i++)
{
y[i]=((x[i-1]^(y[i-1]/*%maxh[i-1]*/))|(x[i]/*%maxh[i-1]*/))^(x[i]/*%maxh[i-1]*/);
}
for (int i=1;i<=n;i++) printf("%lld ",y[i]);
printf("\n");
}
// fclose(stdin);fclose(stdout);
return 0;
}
E. Air Conditioners(题目)
题目大意:有\(n\)个房间和\(k\)个空调,第\(A_i\)个房间里有一个空调,空调温度为\(T_i\),第\(i\)个房间的温度为\(min_{j=1}^{k}(|A_j-i|+T_j)\),求每个房间的温度
分析:
- 首先我们可以进行暴力模拟,但这样会超时,所以我们经过观察,发现这个房间的最低温度为它\(Min(左边房间的最低温度+1,右边房间最低温度+1)\),所以我们可以把这个问题分成两部分,分别求\(L_i\)表示左边空调可以让\(i\)号房间能到底的最低温度,右边同理
- 这个问题我们可以用\(DP\)来解决,初始化每个房间都是\(2^{31}-1\),之后使得\(L_{A_i}=T_i\),容易得到转移方程就是\(L_i=min(L_{i-1}+1,L_i)\),所以每个房间的温度就是\(min(L_i,R_i)\)
代码实现:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
void read(int &sum)
{
sum=0;char last='w',ch=getchar();
while (ch<'0' || ch>'9') last=ch,ch=getchar();
while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
if (last=='-') sum=-sum;
}
int q;
int n,k;
int t[3*100010];
int l[3*100010],r[3*100010];
struct node { int x,t; }a[3*100010];
bool cmp(node a,node b) { return a.t<b.t; }
int main()
{
// freopen("M.in","r",stdin);
// freopen("M.out","w",stdout);
read(q);
while (q--)
{
memset(l,63,sizeof(l));
memset(r,63,sizeof(r));
read(n);read(k);
for (int i=1;i<=k;i++) read(a[i].x);
for (int i=1;i<=k;i++) read(a[i].t),l[a[i].x]=r[a[i].x]=a[i].t;
for (int i=1;i<=n;i++) l[i]=min(l[i],l[i-1]+1);
for (int i=n;i>=1;i--) r[i]=min(r[i],r[i+1]+1);
for (int i=1;i<=n;i++) t[i]=min(l[i],r[i]);
for (int i=1;i<=n;i++) printf("%d ",t[i]);
printf("\n");
}
// fclose(stdin);fclose(stdout);
return 0;
}
F. Array Stabilization (GCD version)(题目)
题目大意:给出一个数列\(A\),求最小的长度\(len\),使得\(gcd(A_1,A_2,...,A_{1+len-1})=gcd(A_2,A_3,...,A_{2+len-1})=...=gcd(A_n,A_1,...,A_{(n+len-1)\%n})\)
分析:
- 看到求一个区间的\(gcd\),我们容易想到用线段树维护一些区间\(gcd\),之后二分长度就可以求出\(len\),时间复杂度\(O(t*(n*\log_2n+\log_2n*\log_2n))\)
代码实现:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
void read(int &sum)
{
sum=0;char last='w',ch=getchar();
while (ch<'0' || ch>'9') last=ch,ch=getchar();
while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
if (last=='-') sum=-sum;
}
int t,n;
int ans;
struct tree { int l,r,c; }tr[2*100010*8];
int a[2*100010],b[2*100010];
int gcd(int x,int y)
{
if (x%y==0) return y;
else return gcd(y,x%y);
}
void build(int l,int r,int k)
{
tr[k].l=l,tr[k].r=r;
if (l==r) tr[k].c=a[l];
else
{
int mid=(l+r)/2;
build(l,mid,k*2),build(mid+1,r,k*2+1);
tr[k].c=gcd(tr[k*2].c,tr[k*2+1].c);
}
}
int ask(int l,int r,int k,int x,int y)
{
if (r<l) return -1;
if (y<l || r<x) return -1;
if (x<=l && r<=y) return tr[k].c;
int mid=(l+r)/2;
int q=ask(l,mid,k*2,x,y),p=ask(mid+1,r,k*2+1,x,y);
if (q==-1) return p;
if (p==-1) return q;
if (q!=-1 && p!=-1) return gcd(q,p);
else return -1;
}
bool check(int len)
{
// printf("len : %d\n",len);
int mmax=0,mmin=100000000;
for (int i=1;i<=n;i++)
{
int l=i,r=i+len-1;
if (r<=n)
mmax=max(mmax,ask(1,n,1,l,r)),
mmin=min(mmin,ask(1,n,1,l,r));
else
mmax=max(mmax,gcd(ask(1,n,1,l,n),ask(1,n,1,1,(i+len-1)%n))),
mmin=min(mmin,gcd(ask(1,n,1,l,n),ask(1,n,1,1,(i+len-1)%n)));
// printf("%d %d %d\n",l,i+len-1,(i+len-1)%n);
// printf("m : %d %d\n",mmax,mmin);
}
// printf("m : %d %d\n",mmax,mmin);
if (mmax==mmin) return true;
else return false;
}
int main()
{
// freopen("M.in","r",stdin);
// freopen("M.out","w",stdout);
read(t);
while (t--)
{
read(n);
for (int i=1;i<=n;i++) read(a[i]);
build(1,n,1);
int l=1,r=n+1;
while (r-l>1)
{
int mid=(l+r)/2;
if (check(mid)==true) r=mid;
else l=mid+1;
}
// printf("%d %d\n",l,r);
int g;
if (check(r)==true) g=r;
if (check(l)==true) g=l;
printf("%d\n",g-1);
}
// fclose(stdin);fclose(stdout);
return 0;
}
G. How Many Paths?(题目)
题目大意:给出一个有向图,问对于每一个点\(i(1\leqslant i \leqslant n)\),如果点\(1\)到它有一条路径输出\(1\),两条或以上(有限个)输出\(2\),无数条输出\(-1\),不能到底输出\(0\)
分析:对于一个有环的路径,就是无数条的,于是我们只有用\(tarjan\)把环判掉,所有经过环的路径到达的点都是\(-1\),其他的\(bfs\)统计一些个数就可以了。
代码实现:
#include<bits/stdc++.h>
using namespace std;
const int N=400010,M=400010;
struct node {
int u,v,next;
} a[M];
int n,m,h[N],cnt,top,dfn[N],low[N],vis[N],ans[N];
stack<int> st;
void add(int u,int v)
{
a[++top]=(node){u,v,h[u]}; h[u]=top;
}
void solve(int x,int k)
{
if (k==-1) ans[x]=-1;
if (ans[x]==-1) return;
ans[x]=min(ans[x]+k,2);
}
void tarjan(int u)
{
dfn[u]=low[u]=++cnt;
st.push(u);
vis[u]=1;
for (int v,tmp=h[u];tmp;tmp=a[tmp].next) {
v=a[tmp].v;
if (v==u) ans[u]=-1;
if (!dfn[v]) {
tarjan(v);
low[u]=min(low[u],low[v]);
} else if (vis[v]) low[u]=min(low[u],dfn[v]);
}
if (dfn[u]==low[u]) if (st.top()!=u) {
int x;
do {
x=st.top(); st.pop();
ans[x]=-1;
vis[x]=0;
} while (x!=u);
} else {
vis[st.top()]=0; st.pop();
}
}
void dfs(int u)
{
if (vis[u]) return;
vis[u]=1;
for (int v,tmp=h[u];tmp;tmp=a[tmp].next) {
v=a[tmp].v;
solve(v,ans[u]);
dfs(v);
}
}
void dfs2(int u)
{
for (int v,tmp=h[u];tmp;tmp=a[tmp].next) {
v=a[tmp].v;
if (ans[v]==1) {
ans[v]=2;
dfs2(v);
}
}
}
int main()
{
int T;
scanf("%d",&T);
while (T--) {
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) h[i]=dfn[i]=low[i]=vis[i]=ans[i]=0;
while (!st.empty()) st.pop();
top=0;
for (int u,v,i=1;i<=m;i++) {
scanf("%d%d",&u,&v);
add(u,v);
}
tarjan(1);
solve(1,1);
for (int i=1;i<=n;i++) vis[i]=0;
for (int i=1;i<=n;i++) if (ans[i]==-1) dfs(i);
dfs(1);
for (int i=1;i<=n;i++) if (ans[i]==2) dfs2(i);
for (int i=1;i<=n;i++) printf("%d ",ans[i]); printf("\n");
}
return 0;
}