Atcoder Regular Contest 108详细题解(A-F,含代码)

Solution

A

我们对于两个乘积为 P P P 的约数配对,看看这些对中是否存在两个数的和为 S S S 即可。

时间复杂度 O ( P ) O(\sqrt P) O(P )

B

一道有一点坑的题目,首先放上一组Hack数据:

Input:
ffoxfoxox

Output:
3 //it is wrong

Answer:
0 //jury's answer

进入正题。

思路比较显然:只需要找到所有连续子串 f o x fox fox,然后向两边扩展即可。更详细地说,对于一个串 f o f o f f o x o x o fofoffoxoxo fofoffoxoxo,我们先找到中间的 f o x fox fox,然后扩展得到 f f o x o x ffoxox ffoxox(左边多了"f",右边多了"ox";可以发现,通过删去中间的 f o x fox fox f f f o x ox ox可以拼接成新的 f o x fox fox并进行删除,于是此扩展合法),再扩展到 f o f f o x o x foffoxox foffoxox。继续尝试扩展,发现扩展失败,立即停止。最终答案为串长 ( n ) (n) (n)减去删去的 f o x fox fox的数量乘 3 3 3

可以发现,我们向外扩展相当于不停地查询前驱与后继(并不是它之前的那个数与它之后的那个数,因为它前面的几个数可能会被删除,后面同理);同时还要不停地删除串。我们可以采用链表维护。

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

C

首先,我们会发现一个性质: 如果给定的图是一棵树,那么一定存在一种方案满足要求。

首先,根节点乱填一个,向下深搜。如果当前这个节点的父节点不等于当前这个节点的父边的权值,那么这个节点就必须填上它父边的权值;否则乱填一个,但是要保证这个节点填上的数要与父节点所填的数不同。通过这样的策略,最终一定能得到一个合法的方案。

既然树的情况解决了,就可以轻松扩展到图的情况了。我们跑出图的一棵生成树,再按照上述方式填即可。生成树可以采用并查集跑出。

时间复杂度 O ( n + m log ⁡ n ) O(n+m \log n) O(n+mlogn)。如果采用按秩合并并查集,可以优化到 O ( n + m ) O(n+m) O(n+m)

D

数据加强: n ≤ 1 0 18 n≤10^{18} n1018,时限 1 s 1s 1s,空限 256 M B 256MB 256MB

看到题目,发现只有 a a , a b , b a , b b aa,ab,ba,bb aa,ab,ba,bb四种变量,可以想到大力分讨。可是有 16 16 16种呐!没事,耐心来!

c A B = A c_{AB}=A cAB=A

(1) 如果 c A B = A , c A A = A c_{AB}=A, c_{AA}=A cAB=A,cAA=A,那么最终形成的字符串一定是 A A A . . . B AAA...B AAA...B,即答案为 1 1 1


(2) 假设 c A A = B c_{AA}=B cAA=B.

1°: c B A = A c_{BA}=A cBA=A
我们在草稿纸上,使用 c A B = A c_{AB}=A cAB=A, c A A = B c_{AA}=B cAA=B, c B A = A c_{BA}=A cBA=A创造出属于自己的几个作品:
A A B A B AABAB AABAB
A B A B A A A B ABABAAAB ABABAAAB
A B A A B A A A A B A A B A A A B A B A B ABAABAAAABAABAAABABAB ABAABAAAABAABAAABABAB

观察一下我们随意画出来的两个字符串,可以发现,此时所画出来的串满足

  1. 开头的那个字母是是 A A A
  2. 结尾的那两个字母是 A B AB AB
  3. 不存在连续的两个 B B B

这三个组成了充分必要条件
于是,我们这个串的形式也就是 A A A______ A B AB AB,中间的横线上填的是一个长度为 n − 3 n-3 n3,且没有任何两个连续的 B B B的字符串。

所以,这种情况的答案为不含连续 0 0 0 01 01 01序列的个数,即斐波那契数列的第 n − 2 n-2 n2项。


2°: c B A = A c_{BA}=A cBA=A

我们仍然通过自由创作的方式,可以发现两个性质,组成了充分必要条件

  1. 开头的那个字母是是 A A A
  2. 结尾的那两个字母是 A B AB AB

即,确定了开头的一个与结尾的两个字母,中间的并不确定。根据乘法原理,这种情况的答案为 2 n − 3 2^{n-3} 2n3

c A B = B c_{AB}=B cAB=B

类比①,很容易得到这一部分的答案。

(1)如果 c B B = B c_{BB}=B cBB=B,只能形成 A B B B . . . . B ABBB....B ABBB....B,答案为 1 1 1

(2)如果 c B B = A c_{BB}=A cBB=A

1°: 如果 c B A = A c_{BA}=A cBA=A,答案为 2 n − 3 2^{n-3} 2n3
2°: 如果 c B A = B c_{BA}=B cBA=B,答案为斐波那契数列的第 n − 2 n-2 n2项。

综上所述,我们采用矩阵快速幂计算斐波那契数列的第 n − 2 n-2 n2项,采用快速幂计算 2 n − 3 2^{n-3} 2n3,即可在 O ( log ⁡ n ) O(\log n) O(logn)的复杂度内通过本题。

本题巧妙运用了多种性质,大力分类讨论,考验了找规律能力与观察能力,是一道不可多得的好题。自己并没有想出来,主要是不够耐心去大力分讨,而且缺乏观察力,实在是太菜了。

E

考虑区间 dp \text{dp} dp

f l , r f_{l,r} fl,r 表示,考虑区间 [ l , r ] [l,r] [l,r],选出的集合大小的期望值。在此,我们默认 l , r l,r l,r 均在选出的集合中。

( l , r ) (l,r) (l,r) 中有 k k k 个位置 p p p 满足 a l ≤ a p ≤ a r a_l \le a_p \le a_r alapar,那么下一步中有 k k k 种选择方案,选择每一种方案的概率都是 1 k \frac 1 k k1。枚举这些位置 p p p,得到

f l , r = 1 k ∑ p = l + 1 , a l ≤ a p ≤ a r r − 1 ( f l , p + f p , r ) f_{l,r}=\frac 1 k\sum_{p=l+1, a_l \le a_p \le a_r}^{r-1} (f_{l,p}+f_{p,r}) fl,r=k1p=l+1,alaparr1(fl,p+fp,r)

采用树状数组优化即可做到 O ( n 2 log ⁡ n ) O(n^2 \log n) O(n2logn)

F

首先,我们找到树的一条直径,并假设这个直径的两端为 u , v u,v u,v。显然,如果 u , v u,v u,v同色,那么答案就是直径的长度。对于这一种直径两端同色的情况对答案的贡献,显然是 2 × 2 n − 2 × d i s 2×2^{n-2}×dis 2×2n2×dis,这里 d i s dis dis表示直径的长度。之所以前面还要乘上一个系数 2 2 2,是因为 u , v u,v u,v可以均填黑均填白。

处理并排除这种情况之后,一定满足直径的两端异色

Theory

无论我们将剩下的节点如何涂色,所有白色节点间的一个最长距离的一端一定是 u , v u,v u,v中是白色的那一个;黑色同理。

根据这个引理,可以完成一个转化。我们记 d i , 0 d_{i,0} di,0表示 i i i u u u的距离, d i , 1 d_{i,1} di,1表示 i i i v v v的距离;我们要对于每一个 i i i均选择 d i , 0 d_{i,0} di,0 d i , 1 d_{i,1} di,1中的一个,要求的就是所有方案中所选的数的最大值之和。

如果快速求出呢?我们考虑枚举这个最大值,求出存在多少种方案满足所选的数的最大值等于它。这个似乎很难求……于是我们采用前缀和的思想,尝试快速求出“存在多少种方案满足所选的数的最大值不大于它”,即“存在多少种方案满足所选的数的均不大于它”。

x i x_i xi表示存在多少种方案满足所选的数的均不大于 i i i,那么 x i = ∏ j = 1 n ( ∑ k = 0 1 [ d j , k ≤ i ] ) x_i=\prod_{j=1}^n (\sum_{k=0}^1 [d_{j,k}≤i]) xi=j=1n(k=01[dj,ki])

如果存在某一对 d i , 0 , d i , 1 d_{i,0},d_{i,1} di,0,di,1均大于 k k k,那么 x k = 0 x_k=0 xk=0。处理了这一种情况后,再令 m i = m a x ( d i , 0 , d i , 1 ) m_i=max(d_{i,0},d_{i,1}) mi=max(di,0,di,1),那么 x i = ∏ j = 1 n ( [ m j ≤ i ] + 1 ) x_i=\prod_{j=1}^n ([m_j≤i]+1) xi=j=1n([mji]+1)

直接对 m m m排序,然后双指针扫一遍即可求出所有 x i x_i xi的值。

答案就是 2 ( ∑ i = 1 d i s ( x i − x i − 1 ) i + d i s 2 n − 2 ) 2 (\sum_{i=1}^{dis} (x_i-x_{i-1}) i + dis 2^{n-2}) 2(i=1dis(xixi1)i+dis2n2)

这里 d i s dis dis为直径长度。

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


首先,看到本题是一个有关路径的最长问题,往往要尝试将其与性质多多的直径牵扯上关系。然后,我们完成了转化,再通过一些小技巧完美地 O ( n ) O(n) O(n)解决了本题。

Code

A

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

int n,m;

signed main()
{
	cin>>n>>m;
	for (int i=1;i*i<=m;i++)
	{
		if (m%i==0)
		{
			int y=m/i;
			if (i+y==n)  return cout<<"Yes"<<endl,0;
		}
	}
	cout<<"No"<<endl;
	
	return 0;
}

B

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

int n,ans=0,vi[500005],frt[500005],nxt[500005];
char a[500005];

void del(int x)
{
	frt[nxt[x]]=frt[x];
	nxt[frt[x]]=nxt[x];
}

signed main()
{
	cin>>n;
	for (int i=1;i<=n;i++)  cin>>a[i],frt[i]=i-1,nxt[i]=i+1;
	for (int i=1;i<=n-2;i++)
	{
		if (vi[i])  continue;
		if (a[i]=='f'&&a[nxt[i]]=='o'&&a[nxt[nxt[i]]]=='x')
		{
			int head=frt[i],tail=nxt[nxt[nxt[i]]];
			del(i),del(nxt[i]),del(nxt[nxt[i]]);
			ans++;
			while (head>=1)
			{
				if (a[head]=='o'&&a[frt[head]]=='f'&&a[tail]=='x')
				{
					del(head),del(frt[head]),del(tail);
					head=frt[frt[head]],tail=nxt[tail],ans++;
				}
				else if (a[head]=='f'&&a[tail]=='o'&&a[nxt[tail]]=='x')
				{
					del(head),del(tail),del(nxt[tail]);
					head=frt[head],tail=nxt[nxt[tail]],ans++;
				}
				else break;
			}
		}
	}
	cout<<n-3*ans<<endl;
	
	return 0;
}

C

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

int n,m,cnt=0;
int fa[200005],head[200005],ans[200005];

struct edge
{
	int next,to,dis;
}e[400005];

struct node
{
	int u,v,w;
}a[200005];

void add_edge(int u,int v,int dis)
{
	cnt++;
	e[cnt].to=v;e[cnt].next=head[u];e[cnt].dis=dis;
	head[u]=cnt;
}

void dfs(int now,int fath,int pa)
{
	if (now!=1)
	{
		if (ans[fath]==pa)
		{
			ans[now]=1;
			if (ans[now]==pa)  ans[now]++;
		}
		else ans[now]=pa;
	}
	for (int i=head[now];i;i=e[i].next)
	{
		int y=e[i].to;
		if (y==fath)  continue;
		
		dfs(y,now,e[i].dis);
	}
}

int fin(int x)
{
	if (x==fa[x])  return x;
	else return fa[x]=fin(fa[x]);
}

signed main()
{
	cin>>n>>m;
	for (int i=1;i<=m;i++)  cin>>a[i].u>>a[i].v>>a[i].w;
	for (int i=1;i<=n;i++)  fa[i]=i;
	for (int i=1;i<=m;i++)
	{
		int x=a[i].u,y=a[i].v;
		int fx=fin(x),fy=fin(y);
		
		if (fx!=fy)
		{
			add_edge(x,y,a[i].w),add_edge(y,x,a[i].w);
			fa[fx]=fy;
		}
	}
	ans[1]=1;
	dfs(1,0,0);
	for (int i=1;i<=n;i++)  cout<<ans[i]<<endl;
	
	return 0;
}

D

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;

int n,ans,dp[100005];
char a,b,c,d;

int quick_power(int x,int y)
{
	int res=1;
	for (;y;y=y>>1,x=(x*x)%mod)
	  if (y&1)  res=(res*x)%mod;
	
	return res;
}

signed main()
{	
	cin>>n>>a>>b>>c>>d;
	dp[0]=1,dp[1]=2;
	for (int i=2;i<=n;i++)  dp[i]=(dp[i-1]+dp[i-2])%mod;
	if (n==2)  return cout<<1<<endl,0;
	if (b=='B')
	{
		if (d=='B')  ans=1;
		else if (d=='A'&&c=='A')  ans=quick_power(2,n-3);
		else if (d=='A'&&c=='B')  ans=dp[n-3];
	}
	else if (b=='A')
	{
		if (a=='A')  ans=1;
		else if (a=='B'&&c=='A')  ans=dp[n-3];
		else if (a=='B'&&c=='B')  ans=quick_power(2,n-3);
	}
	cout<<ans<<endl;
	
	return 0;
}

F

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;

int n,u,v,maxt=0,cnt=0,ans=0,pt,lim,tmp=0,pos=0;
int head[200005],dis[200005][2],maxv[200005],p[200005],q[200005];

struct edge
{
	int next,to;
}e[400005];

void init()
{
	p[0]=1;
	for (int i=1;i<=n;i++)  p[i]=(p[i-1]*2)%mod;
}

void add_edge(int u,int v)
{
	cnt++;
	e[cnt].to=v;e[cnt].next=head[u];head[u]=cnt;
}

void dfs(int now,int fath,int d)
{
	if (d>maxt)  maxt=d,u=now;
	for (int i=head[now];i;i=e[i].next)
	  if (e[i].to!=fath)  dfs(e[i].to,now,d+1);
}

void dfs2(int now,int fath,int d)
{
	if (d>maxt)  maxt=d,v=now;
	dis[now][0]=d;
	
	for (int i=head[now];i;i=e[i].next)
	  if (e[i].to!=fath)  dfs2(e[i].to,now,d+1);
}

void dfs3(int now,int fath,int d)
{
	dis[now][1]=d;
	lim=max(lim,min(dis[now][0],dis[now][1]));
	for (int i=head[now];i;i=e[i].next)
	  if (e[i].to!=fath)  dfs3(e[i].to,now,d+1);
}

signed main()
{
	cin>>n;
	init();
	for (int i=1;i<n;i++)
	{
		cin>>u>>v;
		add_edge(u,v),add_edge(v,u);
	}
	dfs(1,0,0);maxt=0;
	dfs2(u,0,0);
	dfs3(v,0,0);

	for (int i=1;i<=n;i++)
	{
		if (i!=u&&i!=v)  maxv[++pos]=max(dis[i][0],dis[i][1]);
	}
	pt=pos;
	sort(maxv+1,maxv+pos+1);
	for (int i=maxt;i>=lim;i--)
	{
		while (pt>=1&&maxv[pt]>i)  pt--;
		q[i]=p[pt];
	}
	for (int i=lim;i<=maxt;i++)
	{
		q[i]=((q[i]-tmp)%mod+mod)%mod;
		tmp=(tmp+q[i])%mod;
		ans=(ans+q[i]*i)%mod;
	}
	ans=(ans+maxt*p[n-2])%mod;
	cout<<(ans*2)%mod<<endl;
	
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值