Codeforces Round #731 (Div. 3)

Codeforces Round #731 (Div. 3)

Problem A. Shortest Path with Obstacle

本题有\(t\)组数据。

\(A(x_1,y_1),B(x_2,y_2),F(x_3,y_3)\)

要求从\(A\)走到\(B\),路径中不能经过\(F\)

求最短路长度。

对于\(100\%\)的数据:\(1\leq t \leq 10^4 , 1 \leq x_i,y_i \leq 10^4\)

如果\(A,B,F\)在同一水平或者竖直线上,且\(F\)\(A,B\)之间,答案为\(D(A,B)+2\)

否则答案为\(D(A,B)\)。其中\(D(A,B)\)表示点\(A,B\)之间的最短路长度。

\(D(A,B)=|x_A-x_B| + |y_A-y_B|\)

时间复杂度\(O(t)\)

# include <bits/stdc++.h>
using namespace std;
int main()
{
	int t; scanf("%d",&t);
	while (t--) {
		int x1,y1; scanf("%d%d",&x1,&y1);
		int x2,y2; scanf("%d%d",&x2,&y2);
		int x3,y3; scanf("%d%d",&x3,&y3);
		int ans;
		if (x1==x2&&x2==x3) {
			if (y1>y2) swap(y1,y2);
			if (y1<=y3&&y3<=y2) ans=y2-y1+2;
			else ans=y2-y1;
			printf("%d\n",ans);
			continue;
		}
		if (y1==y2&&y2==y3) {
			if (x1>x2) swap(x1,x2);
			if (x1<=x3&&x3<=x2) ans=x2-x1+2;
			else ans=x2-x1;
			printf("%d\n",ans);
			continue;
		}
		ans=abs(y1-y2)+abs(x1-x2);
		printf("%d\n",ans);	
	}
	return 0;
}

Problem B. Alphabetical Strings

本题有\(t\)组数据。

若一个长度为\(n\)的字符串\(s\)是按如下方式构造的:

\(i\)次操作将字母表第\(i\)个字符放在当前字符串的最左侧或最右侧。

现在给出一个字符串\(a\),判断其是否按照上述方法构造。

对于\(100\%\)的数据:\(1\leq t \leq 10^4 , 1 \leq length(a) \leq 26\)

直接模拟,倒序双指针判断当前字符串左右侧字母是否为应该的字母。

直到字符串中所有字母都被判断。

时间复杂度\(O(tlength(a))\)

# include <bits/stdc++.h>
using namespace std;
char s[27];
bool solve() {
	int len=strlen(s);
	int l=0,r=len-1;
	for (int i=len-1;i>=0;i--) {
		if (s[l]=='a'+i) l++;
		else if (s[r]=='a'+i) r--;
		else return 0;
	}
	return 1;
}
int main()
{
	int t; scanf("%d",&t);
	while (t--) {
		scanf("%s",s); 
		if (solve()) puts("YES"); else puts("NO");	
	} 
	return 0;
}

Problem C. Pair Programming

本题有\(t\)组数据。

数组\(a_n,b_m\),要求数组\(c_{n+m}\),其中 \(c_i \in \{ a_n,b_m \}\)\(c_{n+m}\)中有子序列\(a_n,b_n\)

\(c_i=0\)则新增\(1\)行,否则访问第\(c_i\)行。

若存在\(c\)则输出该数组,否则输出\(-1\)

对于\(100\%\)的数据:\(1\leq t \leq 10^3 , 1 \leq n,m \leq 100, 1 \leq a_i,b_i \leq 300\)

双指针,用\(pt1\)指向\(a\)数组,\(pt2\)指向\(b\)数组。

由于访问无后效性,且新增\(1\)行对后面有更加宽松的限制,所以贪心,能新增就新增一行。

时间复杂度\(O(t(n+m))\)

# include <bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int k,n,m,ans[N],a[N],b[N];
bool work() {
	int p1=1,p2=1;
	while (p1<=n||p2<=m) {
		if (p1<=n&&p2<=m&&a[p1]>k&&b[p2]>k) return false;
		if (p1>n&&p2<=m&&b[p2]>k) return false;
		if (p2>m&&p1<=n&&a[p1]>k) return false;
		if (p1<=n) {
			if (a[p1]==0) ans[++ans[0]]=a[p1],k++,p1++;
			else if (a[p1]<=k) ans[++ans[0]]=a[p1],p1++;	
		}
		if (p2<=m) {
			if (b[p2]==0) ans[++ans[0]]=b[p2],k++,p2++;
			else if (b[p2]<=k) ans[++ans[0]]=b[p2],p2++;	
		}
	}
	return true;
}
int main()
{
	int t; scanf("%d",&t);
	while (t--) {
		ans[0]=0;
		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]);
		if (work()==false) puts("-1");
		else {
			for (int i=1;i<=ans[0];i++) printf("%d ",ans[i]);
			puts("");
		}	
	}
	return 0;
}

Problem D. Co-growing Sequence

定义一个数列\(a_n\)\(growing\)的,则对于\(2 \leq i \leq n\)都满足\(a_i \ and \ a_{i-1} = a_i\)

给出数组\(x_n\),要求字典序最小的\(y_n\)满足\(z_n = x_n \ xor \ y_n\)数列\(z_n\)\(growing\)的。

对于\(100\%\)的数据\(1 \leq n,\sum n \leq 2\times 10^5, 1\leq x_i \leq 2^{30}\)

由亦或的性质可知\(a \ xor \ b = c\)\(a \ xor \ c = b\)等价。

显然\(z_1=a_1\)可以让\(y_1=0\)字典序最小。

下面考虑\(2 \leq i \leq n\),考虑第\(j\in[0,30]\)个二进制位。

如果\(z_{i-1}\)\(j\)个二进制位位\(1\),那么\(z_i\)的第\(j\)个二进制位必须为\(1\)才能满足\(z_n\)\(growing\)的。

否则,如果\(a_i\)的第\(j\)个二进制位为\(1\),那么\(z_i\)的第\(i\)个二进制为\(1\),才能使\(y_i\)字典序较小。

因此,可以通过\(x_n\)计算出\(z_n\),那么\(y_n=x_n \ xor \ z_n\)

时间复杂度\(O(n log_2 n)\)

# include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,a[N],b[N],c[N],po[31];
int main()
{
	po[0]=1;
	for (int i=1;i<=30;i++) po[i]=po[i-1]*2;
	int t; scanf("%d",&t);
	while (t--) {
		int n; scanf("%d",&n);
		for (int i=1;i<=n;i++) scanf("%d",&a[i]);
		c[1]=a[1];
		for (int j=0;j<=30;j++) {
			for (int i=2;i<=n;i++)
				if ((c[i-1]&po[j])>0) c[i]=c[i]|po[j];
				else if ((a[i]&po[j])>0) c[i]=c[i]|po[j];
		}
		for (int i=1;i<=n;i++) b[i]=a[i]^c[i];
		for (int i=1;i<=n;i++) printf("%d ",b[i]);
		puts("");
		for (int i=1;i<=n;i++) c[i]=0;
	}
	return 0;
}

Problem E. Air Conditioners

长度为\(n\)的房间,有\(m\)个空调,第\(i\)个空调在\(a_i\)位置,温度为\(t_i\)

\(i\)个房间的温度为\(\min_{1\leq j \leq k} (t_j + |a_j - i|)\) 求所有房间的温度。

对于\(100\%\)的数据,\(1\leq m\leq n \leq 3 \times 10^5\).

我们考虑每一台空调\(t_i,a_i\)对所有房间的贡献。

  • 对于这台空调左侧的所有房间\(j\),温度都会有一个\(T_j = t_i+a_i-j\)的贡献。

  • 对于这台空调右侧的所有房间\(j\),温度都会有一个\(T_j = t_i-a_i+j\)的贡献。

我们维护\(f_j= T_j+j,g_j=T_j-j\),初始条件\(f_{a_i}=t_i+a_i,g_{a_i}=t_i-a_i\)

从左往右遍历\(i\),可以保证当前空调影响后面房间的限制,所以\(g_i=min(g_i,g_{i-1})\)

从右往左遍历\(i\),可以保证当前空调影响前面房间的限制,所以\(f_i=min(f_i,f_{i+1})\)

对于每一个房间\(i\),其温度为\(min(f_i-i,g_i+i)\)

时间复杂度\(O(n)\)

# include <bits/stdc++.h>
# define inf (2e9)
using namespace std;
const int N=3e5+10;
int a[N],t[N],n,m,g[N],f[N];
int main()
{
	int T; scanf("%d",&T);
	while (T--) {
		scanf("%d%d",&n,&m);
		for (int i=1;i<=n;i++) g[i]=inf,f[i]=inf;
		for (int i=1;i<=m;i++) scanf("%d",&a[i]);
		for (int i=1;i<=m;i++) scanf("%d",&t[i]);
		for (int i=1;i<=m;i++) {
			f[a[i]]=t[i]+a[i];
			g[a[i]]=t[i]-a[i];
		}
		for (int i=2;i<=n;i++) g[i]=min(g[i],g[i-1]);
		for (int i=n-1;i>=1;i--) f[i]=min(f[i],f[i+1]);
		for (int i=1;i<=n;i++) 
			printf("%d ",min(f[i]-i,g[i]+i));
		puts("");	
	}
	return 0;
}

Problem F. Array Stabilization (GCD version)

\(n\)个正整数\(a_i\)组成的圆环,每一次操作可以让\(a'_i=gcd(a_i,a_{(i+1) \ mod \ n})\)

询问最少多少次操作能将该圆环所有元素变为相等。

对于\(100\%\)的数据,\(2\leq n \leq 2 \times 10^5,1\leq a_i\leq 10^6\)

显然,最终所有元素都为\(gcd(a_1,a_2,...,a_n)\).

将所有元素同除以\(gcd\)后,则需要操作次数不变,所有元素最终变为\(1\)

对于特定的一个元素\(i\),设最小需要\(ans_i\)次变为\(1\),这等于其和右侧连续最少数的\(gcd\)值变为\(1\)

对于每一个数\(a_i\)分解质因数,从\(i=1\)开始向\(i=n\)扫描,对于每一个\(a_i\),设分解出的质因子为\(p_j\)

则暴力向右侧扫描,若遇到连续的数能被\(p_j\)整除,则加上答案,并将这个数除去\(p_j\)让它不存在该质因子。

设最终扫描区间为\([L,R]\),在该区间中,统计\(k\in[L,R]\)的答案。

最终的答案为\(max(ans_i)\)

时间复杂度\(O(k n)\),其中\(k\leq 8\),由于\(2\times 3 \times 5 \times 7\times 11 \times 13 \times 17 \times 19> 10^6\)

# include <bits/stdc++.h> 
using namespace std;
const int L=1e6+10,N=4e5+10;
int min_p[L],pr[L],a[N],n,ans[N];
vector<int>p[N]; 
bool is_pr[L];
void EouLaSha(int Lim)
{
    memset(is_pr,true,sizeof(is_pr));
    is_pr[1]=false;
    for (int i=2;i<=Lim;i++) {
        if (is_pr[i]) pr[++pr[0]]=i,min_p[i]=i;
        for (int j=1;j<=pr[0]&&i*pr[j]<=Lim;j++) {
            is_pr[i*pr[j]]=false;
            min_p[i*pr[j]]=pr[j];
            if (i%pr[j]==0) break;
        }
    }
}
void fun(int x,int id) {
    if (x==1) return;
    while (x!=1) {
        int u=min_p[x];
        p[id].push_back(u);
        while (x!=1&&x%u==0) x/=u;
    }
}
int gcd(int a,int b) {
	if (!b) return a;
	else return gcd(b,a%b);
}
int main() {
	EouLaSha(1e6);
	int t; scanf("%d",&t);
	while (t--) {
		scanf("%d",&n);
		for (int i=1;i<=n;i++) {
			scanf("%d",&a[i]);
		}
		int g=a[1]; 
		for (int i=2;i<=n;i++) g=gcd(g,a[i]);
		for (int i=1;i<=n;i++) a[i]/=g; 
		for (int i=1;i<=n;i++) a[n+i]=a[i];
		for (int i=1;i<=2*n;i++) fun(a[i],i);
		for (int i=1;i<=n;i++) {
			for (int j=0;j<p[i].size();j++) {
				int r=i+1;
				while (r<=2*n&&a[r]%p[i][j]==0) r++;
				r--;
				for (int k=i,t=r-i+1;k<=r;k++,t--) {
					ans[k]=max(ans[k],t);
					while (a[k]!=1&&a[k]%p[i][j]==0) a[k]/=p[i][j];
				}
			}
		}
		int ct=0;
		for (int i=1;i<=n;i++) ct=max(ct,ans[i]);
		printf("%d\n",ct); 
		for (int i=1;i<=2*n;i++) p[i].clear(),ans[i]=0;
	}
	return 0;
}

Problem G. How Many Paths?

给出\(n\)个点,\(m\)条边的有向、无重边、可能有自环、不保证联通的图\(G\),从\(1\)出发,对于每一个点\(i\)

若不能到达则输出\(0\),若只有\(1\)种路径到达则输出\(1\)

若有有限的多于\(1\)条路径到达,则输出\(2\),而有无数种方法到达则输出\(-1\)

对于\(100\%\)的数据,\(1 \leq n,m \leq 4 \times 10^5\)

考虑\(dfs\)搜索的搜索树的特性。

\(dfs\)时把每一个节点划分成\(0,1,2\)三类:

  • \(0\)表示当前尚未被搜索
  • \(1\)表示以节点为根的\(dfs\)搜索树尚未完全结束。
  • \(2\)表示以节点为根的\(dfs\)搜索树完全结束。

\(dfs\)当前遍历的节点为\(u\),将要去的节点为\(v\)

\(v\)\(0\)类节点,则继续深搜,

\(v\)\(1\)类节点,则至少存在环(以\(v\)为起始\(u\)为结束),因此\(v\)可以走到的所有节点一定有无数种可能。

\(v\)\(2\)类节点,则从\(1\)走到\(v\)既有原先\(df\)s已经遍历过的路径,还有当前的从\(u\)走到\(v\)的路径,所以\(v\)可以走到的节点如果不是有无数个节点,那一定有有限的多于\(1\)的节点。

如果从\(1\)开始\(dfs\)无法遍历到\(i\)节点,则不存在路径到\(i\)

剩下情况就是有有限的\(1\)条路径了。

时间复杂度\(O(n+m)\)

# include <bits/stdc++.h>
using namespace std;
const int N=4e5+10;
struct rec{
	int pre,to;
}a[N];
int n,m,head[N],used[N],tot;
bool onc[N],dd[N];
void del() {
	tot=0;
	for (int i=1;i<=n;i++) {
		head[i]=0;
		used[i]=0;
		onc[i]=false;
		dd[i]=false;
	}
}
void adde(int u,int v) {
	a[++tot].pre=head[u];
	a[tot].to=v;
	head[u]=tot;
} 
void dfs(int u) {
	used[u]=1;
	for (int i=head[u];i;i=a[i].pre) {
		int v=a[i].to;
		if (used[v]==1) onc[v]=1;
		else if (used[v]==2) dd[v]=1;
		else dfs(v);
	}
	used[u]=2;
}
void dfs1(int u) {
	for (int i=head[u];i;i=a[i].pre) {
		int v=a[i].to; 
		if (!onc[v]) {
			onc[v]=1; dfs1(v);
		}
	}
}
void dfs2(int u) {
	for (int i=head[u];i;i=a[i].pre) {
		int v=a[i].to; 
		if (!dd[v]) {
			dd[v]=1; dfs2(v);
		}
	}
}
int main() {
	int t; scanf("%d",&t);
	while (t--) {
		scanf("%d%d",&n,&m);
		for (int i=1;i<=m;i++) {
			int u,v; scanf("%d%d",&u,&v);
			adde(u,v);
		}
		dfs(1);
		for (int i=1;i<=n;i++) {
			if (onc[i]) dfs1(i);
			if (dd[i]) dfs2(i);
		}
		for (int i=1;i<=n;i++) {
			if (!used[i]) printf("0 ");
			else if (onc[i]) printf("-1 ");
			else if (dd[i]) printf("2 ");
			else printf("1 ");
		}
		puts("");
		del();
	}
	return 0;
}
posted @ 2021-07-12 22:27  Maystern  阅读(140)  评论(0编辑  收藏  举报