ARC104 Solution Set

A. Plus Minus

小学数学解二元一次方程组。

/*
他决定要“格”院子里的竹子。于是他搬了一条凳子坐在院子里,面对着竹子硬想了七天,结果因为头痛而宣告失败。
DONT NEVER AROUND . //
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
char buf[1<<21],*p1=buf,*p2=buf;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read()
{
	int x=0,f=1;
	char c=getchar();
	while(c<'0' || c>'9')	f=(c=='-'?-1:f),c=getchar();
	while(c>='0' && c<='9')	x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
	return x*f;
}
void write(int x)
{
	if(x<0)	x=-x,putchar('-');
	if(x>9)	write(x/10);
	putchar(x%10+'0');
}
const int MOD=998244353;
inline int Add(int u,int v){return u+v>=MOD?u+v-MOD:u+v;}
inline int Sub(int u,int v){return u-v>=0?u-v:u-v+MOD;}
inline int Mul(int u,int v){return LL(u)*LL(v)%MOD;}
int QuickPow(int x,int p)
{
	if(p<0)	p+=MOD-1;
	int ans=1,base=x;
	while(p)
	{
		if(p&1)	ans=Mul(ans,base);
		base=Mul(base,base);
		p>>=1;
	}
	return ans;
}
int a,b;
int main(){
	a=read(),b=read();
	write((a+b)/2),putchar(' '),write(a-(a+b)/2);
	return 0;
}

B. DNA Sequence

用烂了的套路。你显然可以把 AGTC 看成 URDL 最后走回原点,哈哈了。

发现 \(n\) 只有 \(5 \times 10^3\),你把 \(A\) 看作 \(1\)\(T\) 看作 \(-1\)\(G\) 看作 \(10^4\)\(C\) 看作 \(-10^4\)。合法的区间就是和为 \(0\) 的区间。

虽然这个题这样就可以做到 \(O(n)\) 了,但是我偏要写 \(O(n^2)\)。哈哈。

/*
他决定要“格”院子里的竹子。于是他搬了一条凳子坐在院子里,面对着竹子硬想了七天,结果因为头痛而宣告失败。
DONT NEVER AROUND . //
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
char s[5005];
int n,sum[5005];
int main(){
	scanf("%d",&n);
	scanf("%s",s+1);
	for(int i=1;i<=n;++i)
	{
		if(s[i]=='A')	sum[i]=sum[i-1]+1;
		else if(s[i]=='T')	sum[i]=sum[i-1]-1;
		else if(s[i]=='G')	sum[i]=sum[i-1]+10000;
		else	sum[i]=sum[i-1]-10000;
	}
	int ans=0;
	for(int i=0;i<=n;++i)	for(int j=i+1;j<=n;++j)	if(sum[i]==sum[j])	++ans;
	printf("%d",ans);
	return 0;
}

C. Fair Elevator

我觉得做着很痛苦。咕咕。

D. Multiset Mean

传说中的题意比题目难的题目。

下面分析复杂度时把 \(n,k\) 看作同阶。

注意到你直接做怎么做都是 \(O(n^5)\) 的。考虑枚举位置 \(i\) 的答案,那么选择 \(i-1\) 就相当于选 \(-1\),选 \(i+1\) 相当于选 \(+1\)。以此类推。

那么我们在 \([1,i-1]\) 里面选出一些数(权值对应 \(i-1,i-2,\cdots,1\)),相当于在 \([1,i-1]\) 里选出一些数,记它们的和为 \(p\),又在 \([i+1,n]\) 里选出一些数(权值对应 \(1,2,\cdots,n-i\)),相当于在 \([1,n-i]\) 里选出一些数,记他们的和为 \(q\)。那么 \(p=q\) 的时候才合法。

这两个问题互相独立且相似,我们可以通过一次 DP 预处理,通过背包解决这个问题。时间复杂度 \(O(n^4)\)

/*
他决定要“格”院子里的竹子。于是他搬了一条凳子坐在院子里,面对着竹子硬想了七天,结果因为头痛而宣告失败。
DONT NEVER AROUND . //
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
char buf[1<<21],*p1=buf,*p2=buf;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read()
{
	int x=0;
	char c=getchar();
	while(c<'0' || c>'9')	c=getchar();
	while(c>='0' && c<='9')	x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
	return x;
}
void write(int x)
{
	if(x>9)	write(x/10);
	putchar(x%10+'0');
}
int MOD;
inline int Add(int u,int v){return u+v>=MOD?u+v-MOD:u+v;}
inline int Sub(int u,int v){return u-v>=0?u-v:u-v+MOD;}
inline int Mul(int u,int v){return LL(u)*LL(v)%MOD;}
int QuickPow(int x,int p)
{
	if(p<0)	p+=MOD-1;
	int ans=1,base=x;
	while(p)
	{
		if(p&1)	ans=Mul(ans,base);
		base=Mul(base,base);
		p>>=1;
	}
	return ans;
}
int n,K,dp[105][505005];
int main(){
	n=read(),K=read(),MOD=read();
	if(n==1)
	{
		write(K%MOD);
		return 0;
	}
	dp[0][0]=1;
	for(int i=1;i<=n;++i)	for(int j=0;j<=K;++j)	for(int k=i*j;k<=i*(i+1)/2*K;++k)	dp[i][k]=Add(dp[i][k],dp[i-1][k-i*j]);
	for(int i=1;i<=n;++i)
	{
		int ans=0,p=i-1,q=n-i;
		for(int j=1;j<=505000;++j)	ans=Add(ans,Mul(dp[p][j],dp[q][j]));
		ans=Mul(ans,K+1);
		ans=Add(ans,K);
		write(ans),puts("");
	}
	return 0;
}

E. Random LIS

不喜欢这个题。先咕了。

F. Visibility Sequence

可以将 \(P\) 抽象成一个树形结构,做法是在序列最左加上一个极大值,加边 \(i \to P_i\) 即可。那么一个结点通过 DFS 序可以抽象成一段区间,想到区间 DP。

注意到我们要求的是 \(P\),跟实际高度没有任何关系,那么我们构出的树深度当然是越小越好。先考虑怎么构造一棵树使得深度最小。首先叶子显然为 \(1\)。然后要满足以下性质:

  • 父亲的高度严格大于儿子的高度;
  • 左边的兄弟不大于右边的兄弟的高度。

记点 \(p\) 的第一个左兄弟为 \(q\),儿子集合为 \(S\),那么一个点的最小高度就是 \(\max(h_q,\max_{u\in S}\{h_u\})\)。显然这种构造方法可以让 \(H,P\) 唯一对应。

那么只需要问有多少种合法构造方案。我们定义 \(dp_{l,r,h}\) 表示,结点 \(l\)\([l,r]\) 中所有点构成的结点的根,并且构成的树的最小深度为 \(h\) 的方案数。

考虑转移,枚举断点 \(i\)。分类讨论:

  • 加入这棵子树,其深度为 \(h-1\),那么根节点被迫将高度提高到 \(h\)(显然这棵子树加入进去是合法的,不然的话之前的树的高度没有这么低),方案数为:

\[dp_{l,r,h} = \sum_{i=l+1}^{r} \left(\sum_{x=1}^{h}dp_{l,i-1,x}\right) dp_{i,r,h-1} \]

  • 加入这棵子树,之前根节点已经有深度为 \(h-1\) 的子树,我们要将这棵子树的深度被迫提高,那么我们加入深度在 \(1 \sim h-1\) 的子树皆可。注意到其深度最后会变成 \(h-1\),要保证这个根 \(i\) 的最大高度要大于等于 \(h-1\)。但是因为加入 \(h-1\) 的子树会和上面的方案算重,所以我们加入的子树只到 \(h-2\)

\[dp_{l,r,h} = \sum_{i=l+1}^{r} \left(\sum_{x=1}^{h-2}dp_{i,r,x}\right) dp_{l,i-1,h} [a_i \geq h-1] \]

注意到里面的求和式可以用前缀和优化。至此我们用 \(O(n^4)\) 的算法解决了问题。

/*
他决定要“格”院子里的竹子。于是他搬了一条凳子坐在院子里,面对着竹子硬想了七天,结果因为头痛而宣告失败。
DONT NEVER AROUND . //
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
char buf[1<<21],*p1=buf,*p2=buf;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read()
{
	int x=0;
	char c=getchar();
	while(c<'0' || c>'9')	c=getchar();
	while(c>='0' && c<='9')	x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
	return x;
}
void write(int x)
{
	if(x>9)	write(x/10);
	putchar(x%10+'0');
}
const int MOD=1e9+7;
inline int Add(int u,int v){return u+v>=MOD?u+v-MOD:u+v;}
inline int Sub(int u,int v){return u-v>=0?u-v:u-v+MOD;}
inline int Mul(int u,int v){return LL(u)*LL(v)%MOD;}
int QuickPow(int x,int p)
{
	if(p<0)	p+=MOD-1;
	int ans=1,base=x;
	while(p)
	{
		if(p&1)	ans=Mul(ans,base);
		base=Mul(base,base);
		p>>=1;
	}
	return ans;
}
int n,a[105],dp[105][105][105],sum[105][105][105];
int main(){
	n=read();
	a[1]=++n;
	for(int i=2;i<=n;++i)	a[i]=read();
	for(int i=n;i;--i)
	{
		dp[i][i][1]=1;
		for(int j=1;j<=min(n,a[i]);++j)	sum[i][i][j]=1;
		for(int j=i+1;j<=n;++j)
		{
			for(int k=2;k<=min(n,a[i]);++k)
			{
				int &cur=dp[i][j][k];
				for(int l=i+1;l<=j;++l)
				{
					/*
					 第一个:加入了一个比较高大的子树。 
					*/
					cur=Add(cur,Mul(sum[i][l-1][k-1],dp[l][j][k-1]));
					/*
					 第二个:加入了一个子树,这个子树要比左兄弟更大。 
					*/
					if(a[l]>=k-1)	cur=Add(cur,Mul(dp[i][l-1][k],sum[l][j][k-1]));
				}
				sum[i][j][k]=Add(sum[i][j][k-1],cur);
			}
		}
	}
	write(sum[1][n][n]);
	return 0;
}
posted @ 2022-03-07 23:07  SyadouHayami  阅读(37)  评论(0编辑  收藏  举报

My Castle Town.