[AtCoder] Beginner Contest 177

比赛简述

ABC 中比较简单的一场,题目也都比较常规

AtCoder Beginner Contest 177

A - Don’t be late

代码实现

#include <bits/stdc++.h>
using namespace std;

int main()
{
	int d,t,s;
	scanf("%d%d%d",&d,&t,&s);
	printf("%s\n",t*s>=d ? "Yes" : "No");
	return 0;
}

B - Substring

题目大意

给出两个串 $S$ 和 $T$,问 $S$ 至少替换多少字符可以使 $T$ 是 $S$ 的子串。

Tag: 暴力

简要题解

$S$ $T$ 长度都只有 1000,直接枚举 $T$ 的所有匹配位置即可。

代码实现

#include <bits/stdc++.h>
using namespace std;

const int M=1005;
char s[M],t[M];

int main()
{
	scanf("%s%s",s,t);
	int n=strlen(s),m=strlen(t);
	int ans=m;
	for(int i=0;i+m<=n;i++)
	{
		int nans=0;
		for(int j=0;j<m;j++)
			nans+=(s[i+j]!=t[j]);
		ans=min(ans,nans);
	}
	printf("%d\n",ans);
	return 0;
}

C - Sum of product of pairs

题目大意

给出 $n$ 长数组 $A$ 求

求 $ \sum_{i=1}^{n-1} \sum_{j=i+1}^{n} A_{i} A_{j} $ 模 $(10^9 + 7)$

Tag: 前缀和

代码实现

#include <bits/stdc++.h>
using namespace std;

const int M=1005;
char s[M],t[M];

int main()
{
	scanf("%s%s",s,t);
	int n=strlen(s),m=strlen(t);
	int ans=m;
	for(int i=0;i+m<=n;i++)
	{
		int nans=0;
		for(int j=0;j<m;j++)
			nans+=(s[i+j]!=t[j]);
		ans=min(ans,nans);
	}
	printf("%d\n",ans);
	return 0;
}

D - Friends

题目大意

给出 $n$ 个人,$m$ 组朋友关系,朋友关系可传递,问至少分多少组,可以使得每组中的人彼此都不是朋友。

Tag: 并查集

简要题解

显然可以用并查集维护各个朋友圈子的大小,答案最终为最大的圈子。

代码实现

#include <bits/stdc++.h>
using namespace std;

const int M=1005;
char s[M],t[M];

int main()
{
	scanf("%s%s",s,t);
	int n=strlen(s),m=strlen(t);
	int ans=m;
	for(int i=0;i+m<=n;i++)
	{
		int nans=0;
		for(int j=0;j<m;j++)
			nans+=(s[i+j]!=t[j]);
		ans=min(ans,nans);
	}
	printf("%d\n",ans);
	return 0;
}

E - Coprime

[AtCoder] Beginner Contest 177 E - Coprime

E - Coprime

题目大意

给出数组 $A$ 如果其中数字两两互质则返回 “pairwise coprime”,如果整个数组 $gcd$ 为 $1$ 则返回 “setwise coprime”,其他情况返回 “not coprime”

简要题解

显然 setwise 很好判断。

对于 pairwise 可以将每个数分解因数,则任意 $A_{i}$ 不和其他的任何项有相同的质因子,于是统计 $A$ 中所有的质因子频率,之后判断 $A_{i}$ 与其他因子频率的关系即可。

这里数组长度 $n \le 10^6$ 比较大,于是采用挂链分解的方式。

复杂度

$T$:$O(C + nlogC)$ 其中 $C \le 10^6$ 为 $A_{i}$ 的范围

$S$:$O(n)$

代码实现

#include <bits/stdc++.h>
using namespace std;

const int M=1e6+5;
int a[M];

int gcd(int a,int b)
{
	return b?gcd(b,a%b):a;
}

int isp[M],pri[M],pre[M],npri=0;
void get_pri()
{
    for(int i=2;i<M;i++)
    {
        if(!isp[i])
        {
            pre[i]=i;
            pri[npri++]=i;
        }
        for(int j=0;j<npri&&pri[j]*i<M;j++)
        {
            isp[i*pri[j]]=1;
            pre[i*pri[j]]=pri[j];
            if(i%pri[j]==0) break;
        }
    }
}
vector<int> get_factor(int nu)
{
    vector<int> fa;
    if(nu==1) return fa; // fa.push_back(1);
    while(nu!=1)
    {
        fa.push_back(pre[nu]);
        nu/=pre[nu];
    }
    return fa;
}

int cnt[M],cnt2[M];

int main()
{
	get_pri();
	int n; scanf("%d",&n);
	for(int i=0;i<n;i++)
		scanf("%d",&a[i]);

	for(int i=0;i<n;i++)
	{
		vector<int> fas=get_factor(a[i]);
		for(int i:fas)
			cnt[i]++;
	}
	int f=1;
	for(int i=0;i<n;i++)
	{
		vector<int> fas=get_factor(a[i]);
		for(int i:fas)
			cnt2[i]++;
		for(int i:fas)
			if(cnt2[i]<cnt[i])
			{
				// cout<<i<<" "<<cnt[i]<<" "<<cnt2[i]<<endl;
				f=0;
				break;
			}
		if(f==0) break;
		for(int i:fas)
			cnt2[i]--;
	}
	if(f)
	{
		printf("pairwise coprime\n");
		return 0;
	}
	int g=a[0];
	for(int i=1;i<n;i++)
		g=gcd(g,a[i]);
	printf("%s\n",g==1 ? "setwise coprime" : "not coprime");
	return 0;
}

F - I hate Shortest Path Problem

[AtCoder] Beginner Contest 177 F - I hate Shortest Path Problem

F - I hate Shortest Path Problem

题目大意

给出一个 $(h+1) \times w$ 的二维矩阵,初始位置可以是第 $0$ 行的任意位置。

每一个格只能往右或下方向移动

每一行 $i$ 区间 $L_{i}$ 到 $R_{i}$ 的格子不能向下走,问到达每一行的最小可能步数

简要题解

$dp[i][j]$ 为到达 $(i,j)$ 位置的最小步数

注意从左侧走到某个格子总不会比从上方走来更好,除非无法从上方走来

$$ \begin{align} dp[i][j] & = dp[i-1][j]+1 & j \lt L[i-1] \ or \ R[i-1] \lt j \\ & = dp[i-1][L[i-1]-1]+1+(j-(L[i-1]-1)) & L[i-1] \le j \le R[i-1] \end{align} $$

会发现 $dp$ 转移都是以区间为单位的,且对应操作都可以用线段树维护

复杂度

$T$:$O((h + w) \log (w))$

$S$:$O(w)$

代码实现

#include <bits/stdc++.h>
using namespace std;

/*

dp[i][j]=dp[i-1][j]+1; (j<L[i-1] or R[i-1]<j)
dp[i][j]=dp[i-1][L[i-1]-1]+1+(j-(L[i-1]-1)) (L[i-1]<=j<=R[i-1])
*/

const int M=2e5+5;
const int INF=0x3f3f3f3f;

struct SegT
{
	struct TN
	{
		int mn,fi,add;
		void app_add(int v)
		{
			add+=v;
			mn+=v;
		}
		void app_set(int v) // arthimic
		{
			add=0;
			fi=v;
			mn=v;
		}
	}tn[M*4];
	int P,L,R,V;
	void push(int o,int l,int mi,int r)
	{
		if(tn[o].fi!=-1)
		{
			tn[o<<1].app_set(tn[o].fi);
			tn[o<<1|1].app_set(tn[o].fi+mi-l+1);
			tn[o].fi=-1;
		}
		if(tn[o].add)
		{
			tn[o<<1].app_add(tn[o].add);
			tn[o<<1|1].app_add(tn[o].add);
			tn[o].add=0;
		}
	}
	void pull(int o)
	{
		tn[o].mn=min(tn[o<<1].mn,tn[o<<1|1].mn);
	}
	void build(int o,int l,int r)
	{
		tn[o].mn=tn[o].add=0;
		tn[o].fi=-1;
		if(l==r)
		{
			if(l==0) tn[o].mn=tn[o].add=INF;
			return;
		}
		int mid=(l+r)>>1;
		build(o<<1,l,mid);
		build(o<<1|1,mid+1,r);
		pull(o);
	}
	void add(int o,int l,int r)
	{
		if(L<=l&&r<=R)
		{
			tn[o].app_add(1);
			return;
		}
		int mid=(l+r)>>1;
		push(o,l,mid,r);
		if(L<=mid) add(o<<1,l,mid);
		if(mid<R) add(o<<1|1,mid+1,r);
		pull(o);
	}
	void set(int o,int l,int r)
	{
		if(L<=l&&r<=R)
		{
			tn[o].app_set(V+l-L+1);
			return;
		}
		int mid=(l+r)>>1;
		push(o,l,mid,r);
		if(L<=mid) set(o<<1,l,mid);
		if(mid<R) set(o<<1|1,mid+1,r);
		pull(o);
	}
	int query(int o,int l,int r)
	{
		if(l==r) return tn[o].mn;
		int mid=(l+r)>>1;
		push(o,l,mid,r);
		if(P<=mid) return query(o<<1,l,mid);
		else return query(o<<1|1,mid+1,r);
	}
}segt;

int main()
{
	int h,w; scanf("%d%d",&h,&w);
	segt.build(1,0,w);
	int l,r;
	for(int i=0;i<h;i++)
	{
		scanf("%d%d",&l,&r);
		if(l!=1)
		{
			segt.L=1; segt.R=l-1;
			segt.add(1,0,w);
		}
		if(r!=w)
		{
			segt.L=r+1; segt.R=w;
			segt.add(1,0,w);
		}
		segt.P=l-1;
		segt.V=segt.query(1,0,w);
		segt.L=l; segt.R=r;
		segt.set(1,0,w);
		int ans=segt.tn[1].mn;
		printf("%d\n",ans>=INF ? -1 : ans);
	}
	return 0;
}

Prev: [AtCoder] Beginner Contest 177 F - I hate Shortest Path Problem
Next: [AtCoder] Beginner Contest 177 E - Coprime