Codeforces #686 C - F 题解

前言

手慢了,再给我10min就能 ak 了/dk

C - Sequence Transformation

题目大意

给出一个数列,选择一个数使得这个数将这个数列划分成的段数最小。

解题思路

直接记录一个数上一次的出现位置,如果上个位置 \(x\) 与当前位置 \(y\) 的距离 \(y-x>1\) ,那么这个数划分成的段数就加一。

Code

const int N=2e5+5;
int t,n,vis[N],sum[N],ok[N];
 
signed main()
{
	t=read();
	while(t--)
	{
		n=read();
		for(int i=1;i<=n;++i)
			vis[i]=0,sum[i]=0,ok[i]=0;
		for(int i=1;i<=n;++i)
		{
			int a=read();
			if(vis[a]!=i-1)
				sum[a]++;
			vis[a]=i;ok[a]=1;
		}
		for(int i=1;i<=n;++i)
			if(vis[i]!=n)
				sum[i]++;
		int ans=1e18;
		for(int i=1;i<=n;++i)
			if(ok[i])
				ans=min(ans,sum[i]);
		printf("%lld\n",ans);
	}
	return 0;
}

D - Number into Sequence

题目大意

构造一个数列使得这个数列每个数都大于 \(1\) 且乘积为 \(n\),后一个能整除前一个,使得这个数列最长。

解题思路

一个显然的想法是先把 \(n\) 质因数分解。

因为分解的质因数都互质,所以如果要满足后一个能整除前一个,后一个就必须由前一个乘上一个质因子。

但是如果数列中的好几个个数由好几个质因子们的乘积,那么不如把它们拆开成几个 质因子 和 一个质因子们的乘积。

所以就相当于找出一个数量最多的质因子 \(p\) ,假如它的数量是 \(q\) ,那么数列的长度最长长度就是 \(q\),即 \(q-1\)\(p\) 和一个 \(\dfrac{n}{p^{q-1}}\)

Code

const int N=1e6+5;
int t,n,m,k,a[N],b[N];
 
struct Node
{
	int x,y;
	bool operator < (const Node &p)const
	{
		return y>p.y;
	}
 }c[N];
 
signed main()
{
	t=read();
	while(t--)
	{
		n=k=read();m=0;
		for(int i=2;i*i<=n;++i)
		{
			if(n%i==0) a[++m]=i;
			while(n%i==0){n/=i,++b[m];}
		}
		if(n>1)a[++m]=n,b[m]=1;
		for(int i=1;i<=m;++i)
			c[i]=(Node){a[i],b[i]};
		sort(c+1,c+1+m);
		int res=c[1].y;
		printf("%lld\n",res);
		for(int i=1;i<=c[1].y-1;++i)
			printf("%lld ",c[1].x),k/=c[1].x;
		printf("%lld\n",k);
		for(int i=1;i<=m;++i)
			a[i]=0,b[i]=0;
	}
	return 0;
}

E - Number of Simple Paths

题目大意

给一个环基树,问有多少条长度大于 \(1\) 的简单路径。

解题思路

显然如果两个点在环基树中的同一个树中,那么他们之间只存在 \(1\) 条简单路径。

如果两个点在环基树的不同树中或有一个在环上,那么他们之间存在 \(2\) 条简单路径(从环的一半边走或从环的另一半边走)。

所以可以先拓扑排序找出环,然后统计出环基树中每棵树的大小。

显然一棵树对答案的贡献就是 树的大小 \(\times\) \((\) 每个点与树内点的路径数 \(+\) 每个点与树外点的路径数 \()\)

设这可数的大小为 \(siz\)\(siz\times ((siz-1) + (n-siz)\times2)\)

Code

const int N=2e5+5;
int t,n,vis[N],on[N],col[N],siz[N],du[N],ans,flag;
 
struct Edge
{
	int v,ne;
}e[N*2];
int head[N],tot;
 
inline void add(int u,int v);
void dfs(int u,int fa,int c);
 
queue<int> qu;
 
signed main()
{
	t=read();
	while(t--)
	{
		n=read();ans=0;
		for(int i=1;i<=n;++i)
		{
			int u=read(),v=read();
			add(u,v);add(v,u);du[u]++,du[v]++;
		}
		while(!qu.empty()) qu.pop();
		for(int i=1;i<=n;++i)
			if(du[i]==1) qu.push(i);
		while(!qu.empty())
		{
			int u=qu.front();
			qu.pop();
			on[u]=1;
			for(int i=head[u];i;i=e[i].ne)
			{
				int v=e[i].v;
				du[v]--;
				if(du[v]==1) qu.push(v);
			}
		}
		int m=0;
		for(int i=1;i<=n;++i)
			if(on[i]==0) dfs(i,0,++m);
		ans=0;
		for(int i=1;i<=m;++i)
			ans+=siz[i]*(siz[i]-1+(n-siz[i])*2);
		printf("%lld\n",ans/2);
		for(int i=1;i<=n;++i)
		{
			head[i]=0;du[i]=0;
			vis[i]=0;on[i]=0;
			col[i]=0;siz[i]=0;
		}
		tot=0;flag=0;
	}
	return 0;
}
 
inline void add(int u,int v)
{
	e[++tot]=(Edge){v,head[u]};
	head[u]=tot;
}
 
void dfs(int u,int fa,int c)
{
	col[u]=c;
	siz[c]++;
	for(int i=head[u];i;i=e[i].ne)
	{
		int v=e[i].v;
		if(v==fa||on[v]==0)
			continue;
		dfs(v,u,c);
	}
}

F - Array Partition

题目大意

把一个数列划分成三段,使得 第一段的 \(\max=\) 第二段的 \(\min=\) 第三段的 \(\max\)

解题思路

我们假设第一段和第二段的分解点是 \(x\),第二段和第三段的分解点是 \(y\)

我们可以枚举第一段和第二段的分界点 \(x\) ,这样就知道了要使第二段的 \(\min\) 和第三段的 \(\max\) 要等于的值。

可以先考虑如何让第二段的 \(\min\) 与 第一段的 \(\max\) 相等。

由于第二段区间 \([x,y]\)\(\min\) 一定是单调不升的,所以可以二分这个位置,这样就能在找到一个区间 \([p,q]\) ,在这个区间选第二段和第三段的分界点,就能保证第二段的 \(\min\) 与 第一段的 \(\max\) 相等。

再考虑如何保证第三段的 \(\min\) 与 第一段的 \(\max\) 相等。

其实刚才求出的区间 \([p,q]\)\(\max\) 也是单调不降的,所以依旧可以二分。

如果能找到,那么直接输出这个方案就行了。

二分的时候要维护区间极值,可以直接套一个 st表 或 线段树。

时间复杂度 \(\text{O}(n\ \log^2n)\)

Code

const int N=2e5+5;
int t,n,a[N],val[N*4],suc[N],flag;
 
void build(int x,int l,int r);
int query(int x,int l,int r,int L,int R);
inline int binary1(int x,int p);
inline int binary2(int x,int l,int r);
 
signed main()
{
	t=read();
	while(t--)
	{
		n=read();
		for(int i=1;i<=n;++i)
			a[i]=read();
		build(1,1,n);
		for(int i=n;i>=1;--i)
			suc[i]=max(suc[i+1],a[i]);
		int now=0;flag=0;
		for(int i=1;i<=n;++i)
		{
			now=max(now,a[i]);
			int pos1=binary1(now,i+1)+1;
			int pos2=binary1(now+1,i+1)+2;
			if(pos1==-1) continue;
			if(pos2==0) pos2=i+2;
			if(pos1>n) pos1=n; 
			if(pos2>n) pos2=n;
			int pos3=binary2(now,pos2,pos1);
			if(pos3==-1) continue;
			if(i<=0||pos3-1-i<=0||n-(pos3-1-i)-i<=0) continue;
			if(suc[pos3]!=now||query(1,1,n,i+1,pos3-1)!=now)
				continue;
			printf("YES\n%lld %lld %lld\n",i,pos3-1-i,n-(pos3-1-i)-i);
			flag=1;break;
		}
		if(flag==0) printf("NO\n");
		for(int i=1;i<=n;++i)
			suc[i]=0;
	}
	return 0;
}
 
void build(int x,int l,int r)
{
	if(l==r)
	{
		val[x]=a[l];
		return;
	}
	build(lc,l,mid);
	build(rc,mid+1,r);
	val[x]=min(val[lc],val[rc]);
}
 
int query(int x,int l,int r,int L,int R)
{
	if(L<=l&&r<=R) return val[x];
	int res=1e18;
	if(L<=mid) res=min(res,query(lc,l,mid,L,R));
	if(mid+1<=R) res=min(res,query(rc,mid+1,r,L,R));
	return res;
}
 
inline int binary1(int x,int p)
{
	int l=p,r=n,res=-2;
	while(l<=r)
	{
		if(query(1,1,n,p,mid)>=x)
		{
			res=mid;
			l=mid+1;
		}
		else r=mid-1;
	}
	return res;
}
 
inline int binary2(int x,int l,int r)
{
	int res=-1;
	while(l<=r)
	{
		if(suc[mid]<=x)
		{
			res=mid;
			r=mid-1;
		}
		else l=mid+1;
	}
	return res;
}
posted @ 2020-11-25 13:01  Blackbird137  阅读(148)  评论(0编辑  收藏  举报