ARC108 游记

ARC108 游记

继续没脸见人

果然这就是罚时吃到饱。/kk

F 好像比 E 要简单。

D - AB

题意简述

给定 (n) 和四个字符 (c_{AA},c_{AB},c_{BA},c_{BB}) ,并且 (c_{AA},c_{AB},c_{BA},c_{BB}in {A,B})

一开始你有一个长度为 (2) 的字符串 AB ,每次操作你都可以选择两个相邻字符 (c_l,c_r) ,并在它们之间插入字符 (c_{c_lc_r}) ,询问你最终可以得到多少种长度为 (n) 的本质不同的字符串。

(2le nle 1000)

题目分析

分类讨论,一开始肯定是在 AB 间插入 (c_{AB})(c_{AB}=A)(c_{AB}=B) 的情况没有什么区别,不妨假设 (c_{AB}=A)

此时字符串会变成 AAAA...AAB ,如果 (c_{AA}=A) ,那么最终只能变成 AAA...AAAB ,答案就是 (1) ,否则 (c_{AA}=B) ,此时可以选择在 AA 之间插入 B 变成 ABA ,如果 (c_{BA}=A) ,说明在最终情况中不能出现 B 相邻的情况,并且第一个字符和倒数第二个字符必须是 A ,最后一个字符必须是 B ,答案也就是 (sum_{ige 0}{n-3-(i-1)choose i}) ,也就是斐波那契数列的第 (n-3) 项,矩乘优化到一个 (log_2),否则如果 (c_{BA}=B) ,说明在最终情况中可以出现 B 相邻的情况,答案也就是 (2^{n-3})

总的复杂度是 (mathcal O(log_2n))

参考代码

赛时代码,当时写的不是最优复杂度,仅供参考。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ch() getchar()
#define pc(x) putchar(x)
template<typename T>inline void read(T&x){
	int f;char c;
	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
}
template<typename T>inline void write(T x){
	static char q[64];int cnt=0;
	if(!x)pc('0');if(x<0)pc('-'),x=-x;
	while(x)q[cnt++]=x%10+'0',x/=10;
	while(cnt--)pc(q[cnt]);
}
const int maxn=1005,mod=1000000007;
int mo(const int x){
	return x>=mod?x-mod:x;
}
int fac[maxn],iac[maxn];
int binom(int n,int m){
	return 1ll*iac[m]*iac[n-m]%mod*fac[n]%mod;
}
char caa[5],cab[5],cba[5],cbb[5];
int main(){
	int n;read(n);if(n==2)return puts("1"),0;fac[0]=fac[1]=iac[0]=iac[1]=1;
	for(int i=2;i<=n;++i)iac[i]=1ll*(mod-mod/i)*iac[mod%i]%mod;
	for(int i=2;i<=n;++i)iac[i]=1ll*iac[i-1]*iac[i]%mod,fac[i]=1ll*fac[i-1]*i%mod;
	scanf("%s%s%s%s",caa,cab,cba,cbb);int ans=0;
	if(cab[0]=='A'){
		if(caa[0]=='A')ans=1;
		else{
			if(cba[0]=='A'){
				ans=1;
				for(int i=1;n-3-(i-1)>=i;++i)
					ans=mo(ans+binom(n-3-(i-1),i));
			}
			else{
				for(int i=0;i<=n-3;++i)
					ans=mo(ans+binom(n-3,i));
			}
		}
	}
	else{
		if(cbb[0]=='B')ans=1;
		else{
			if(cba[0]=='B'){
				ans=1;
				for(int i=1;n-3-(i-1)>=i;++i)
					ans=mo(ans+binom(n-3-(i-1),i));
			}
			else{
				for(int i=0;i<=n-3;++i)
					ans=mo(ans+binom(n-3,i));
			}
		}
	}
	write(ans),pc('
');
	return 0;
}

E Random IS

题意简述

定义大小为 (n) 的排列 (a) 的一个子序列 (b) 是好的当且仅当 (b) 递增,对于 (a) 的子序列 (b) 而言,定义一个数 (i) 是好的当且仅当 (a_i) 加入 (b)(b) 仍然递增,现在给定 (n) 和排列 (a) ,你有一个 (a) 的子序列 (b) ,初始为空,每次操作你会在所有好的数中选择等概率随机一个好的数 (i) 并将 (a_i) 加入 (b) ,询问期望操作次数。

(1le nle 2000)

题目分析

简单期望题,但是自己赛时由于太蠢没有想出来。

题目等价于随机一个排列 (p) ,然后从前往后加数,如果可以加就加,问期望加入了多少个数。

如果当前考虑的是一段区间 ((l,r)) ,如果选择了 (s) ,那么 (s) 就会将其分成两个区间 ((l,s),(s,r)) ,并且在这两个区间中选数是互不影响的,所以可以认为是先在 ((l,s)) 中随机排列然后选数,然后再在 ((s,r)) 中随机排列然后选数,所以它们的期望值是可以直接加起来的。

(dp(l,r)) 表示仅考虑区间 ((l,r)) 并且选择了 (l)(r) 答案的期望,设可选的数为 (s_1,dots,s_k) ,那么转移就是:

[dp(l,r)=1+frac{1}{k}sum_{i=1}^k(dp(l,s_i)+dp(s_i,r)) ]

考虑一个数 (s) 可选当且仅当 (l< s< r,a_l< a_s< a_r) ,显然可以使用数据结构优化,时间复杂度 (mathcal O(n^2log_2n))

参考代码

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ch() getchar()
#define pc(x) putchar(x)
template<typename T>inline void read(T&x){
	int f;char c;
	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
}
template<typename T>inline void write(T x){
	static char q[64];int cnt=0;
	if(!x)pc('0');if(x<0)pc('-'),x=-x;
	while(x)q[cnt++]=x%10+'0',x/=10;
	while(cnt--)pc(q[cnt]);
}
const int maxn=2005,mod=1000000007;int n;
int mo(const int x){
	return x>=mod?x-mod:x;
}
void change(int*tr,int pos,int val){
	while(pos<=n){
		tr[pos]=mo(tr[pos]+val);
		pos+=pos&(-pos);
	}
}
int query(int*tr,int pos){
	int re=0;
	while(pos){
		re=mo(re+tr[pos]);
		pos^=pos&(-pos);
	}
	return re;
}
int tr0[maxn][maxn],tr1[maxn][maxn],sum[maxn][maxn],dp[maxn],a[maxn],inv[maxn];
int main(){
	read(n);++n;
	for(int i=2;i<=n;++i)read(a[i]),++a[i];
	a[1]=1;++n;a[n]=n;inv[1]=1;
	for(int i=2;i<=n;++i)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for(int len=1;len<n;++len){
		for(int l=1,r=l+len;r<=n;++l,++r){
			if(a[r]<a[l])continue;int cnt=query(sum[l],a[r]);
			if(cnt>0){
				dp[l]=mo(1+1ll*mo(query(tr0[l],a[r])+query(tr1[r],n-a[l]+1))*inv[cnt]%mod);
				change(tr0[l],a[r],dp[l]);change(tr1[r],n-a[l]+1,dp[l]);
			}
			change(sum[l],a[r],1);
		}
	}
	write(dp[1]),pc('
');
	return 0;
}

F Paint Tree

题意简述

给定一棵 (n) 个点的数,每个点可能染成黑色也可能染成白色,定义一种染色方案的权值为 所有黑色点中距离最远的两个点的距离 和 所有白色点中距离最远的两个点的距离 的最大值,询问所有可能染色方案的权值之和。

(2le nle 2 imes 10^5)

题目分析

答案等于枚举 (ige 1) ,然后最远距离大于等于 (i) 的方案数之和,大于等于不太好求,考虑求小于等于。

首先找到直径,设直径的两个端点为 (S)(T) ,如果 (S)(T) 颜色相同,那么最远距离就是 (S)(T) 的距离,假设 (S)(T) 颜色不同, (S)(T) 黑,那么白色点的所有最远距离中一定存在一个最远距离的端点是 (S) ,如果需要满足最远距离小于等于 (i) ,那么所有白色点到 (S) 的距离就要小于等于 (i) ,同理所有黑色点到 (T) 的距离也要小于等于 (i) ,如果一个点 到 (S) 的距离 和 到 (T) 的距离 都小于等于 (i) ,那么这个点可黑可白,否则这个点颜色确定,如果一个点 到 (S) 的距离 并且 到 (T) 的距离 都大于 (i) ,说明这种情况根本不存在。

求有多少点可黑可白只需要 dfs 后一遍前缀和就行了,时间复杂度是 (mathcal O(n)) 的。

参考代码

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ch() getchar()
#define pc(x) putchar(x)
template<typename T>inline void read(T&x){
	int f;char c;
	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
}
template<typename T>inline void write(T x){
	static char q[64];int cnt=0;
	if(!x)pc('0');if(x<0)pc('-'),x=-x;
	while(x)q[cnt++]=x%10+'0',x/=10;
	while(cnt--)pc(q[cnt]);
}
const int maxn=200005,mod=1000000007;
int mo(const int x){
	return x>=mod?x-mod:x;
}
struct Edge{
	int v,nt;
	Edge(int v=0,int nt=0):
		v(v),nt(nt){}
}e[maxn*2];
int hd[maxn],num;
void qwq(int u,int v){
	e[++num]=Edge(v,hd[u]),hd[u]=num;
}
int dp[maxn];
int dfs(int u,int fa){
	int re=u;dp[u]=dp[fa]+1;
	for(int i=hd[u];i;i=e[i].nt){
		int v=e[i].v;
		if(v==fa)continue;
		int te=dfs(v,u);
		if(dp[te]>dp[re])
			re=te;
	}
	return re;
}
int cnt[maxn],_2[maxn],ds[maxn],dt[maxn];
int main(){
	int n;read(n);_2[0]=1;
	for(int i=1;i<=n;++i)_2[i]=mo(_2[i-1]<<1);
	for(int i=1;i<n;++i){
		int u,v;
		read(u),read(v);
		qwq(u,v),qwq(v,u);
	}
	int S=dfs(1,0),T=dfs(S,0),n2=1;
	for(int i=1;i<=n;++i)ds[i]=dp[i]-1;dfs(T,0);
	for(int i=1;i<=n;++i)dt[i]=dp[i]-1;int mx=0;
	for(int i=1;i<=n;++i)++cnt[max(ds[i],dt[i])],mx=max(mx,min(ds[i],dt[i]));
	for(int i=1;i<n;++i)cnt[i]+=cnt[i-1];int ans=0;
	for(int i=0;i<mx;++i)ans=mo(ans+_2[n]);
	for(int i=mx;i<dt[S];++i)ans=mo(ans+mo(mod-mo(_2[cnt[i]]<<1)+_2[n]));
	write(ans),pc('
');
	return 0;
}

原文地址:https://www.cnblogs.com/lsq147/p/14023083.html