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} n≤1018,时限 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
观察一下我们随意画出来的两个字符串,可以发现,此时所画出来的串满足
- 开头的那个字母是是 A A A
- 结尾的那两个字母是 A B AB AB
- 不存在连续的两个 B B B。
这三个组成了充分必要条件。
于是,我们这个串的形式也就是
A
A
A______
A
B
AB
AB,中间的横线上填的是一个长度为
n
−
3
n-3
n−3,且没有任何两个连续的
B
B
B的字符串。
所以,这种情况的答案为不含连续 0 0 0的 01 01 01序列的个数,即斐波那契数列的第 n − 2 n-2 n−2项。
2°: c B A = A c_{BA}=A cBA=A
我们仍然通过自由创作的方式,可以发现两个性质,组成了充分必要条件:
- 开头的那个字母是是 A A A
- 结尾的那两个字母是 A B AB AB
即,确定了开头的一个与结尾的两个字母,中间的并不确定。根据乘法原理,这种情况的答案为 2 n − 3 2^{n-3} 2n−3。
② 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}
2n−3
2°: 如果
c
B
A
=
B
c_{BA}=B
cBA=B,答案为斐波那契数列的第
n
−
2
n-2
n−2项。
综上所述,我们采用矩阵快速幂计算斐波那契数列的第 n − 2 n-2 n−2项,采用快速幂计算 2 n − 3 2^{n-3} 2n−3,即可在 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 al≤ap≤ar,那么下一步中有 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,al≤ap≤ar∑r−1(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×2n−2×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=1∏n(k=0∑1[dj,k≤i])
如果存在某一对 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=1∏n([mj≤i]+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=1∑dis(xi−xi−1)i+dis2n−2)
这里 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;
}