AtCoder Grand Contest 024

Preface

ABCD还挺简单,E没想出来太屑了,F题目读完就知道不可做


A - Fairness

稍微玩一下就会发现进行奇数次操作后答案为\(B-A\),否则为\(A-B\)

#include<cstdio>
#define RI register int
#define CI const int&
using namespace std;
int A,B,C; long long k;
int main()
{
	return scanf("%d%d%d%lld",&A,&B,&C,&k),printf("%d",k&1LL?B-A:A-B),0;
}

B - Backfront

容易发现只有单调上升且数字连续的一段子序列才可以固定

把数的位置离散之后找到最长的连续上升段即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
int n,x,a[N],ans=1e9,cur;
int main()
{
	RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&x),a[x]=i;
	for (i=1;i<=n;++i) if (a[i]<a[i-1]) ans=min(ans,n-cur),cur=1; else ++cur;
	return printf("%d",min(ans,n-cur)),0;
}

C - Sequence Growing Easy

容易发现当\(a_1\ne 0\)\(a_{i}>a_{i-1}+1(i\in [2,n])\)时显然无解

显然接下来我们可以把数列分割成若干段严格单调上升的连续段,每一段的答案显然就是最后一个数

#include<cstdio>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
int n,a[N]; long long ans;
int main()
{
	RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]);
	if (a[1]) return puts("-1"),0; for (i=2;i<=n;++i)
	if (a[i]-a[i-1]>1) return puts("-1"),0;
	for (i=2;i<=n+1;++i) if (a[i]<=a[i-1]) ans+=a[i-1];
	return printf("%lld",ans),0;
}

D - Isomorphism Freak

容易想到如果枚举一个点作为根,很容易我们要把整个图补成一个分形

此时需要的颜色数显然就是根到最深叶节点的深度,第二个点就是每个深度下分叉数取个\(max\)再做积

然后我们发现样例中还有一种情况是选定一条边的两个端点为根的,因此我们还要枚举每条边

总体复杂度\(O(n^2)\)

#include<cstdio>
#include<utility>
#include<iostream>
#define RI register int
#define CI const int&
#define mp make_pair
using namespace std;
typedef pair <int,long long> pi;
const int N=105;
struct edge
{
	int to,nxt;
}e[N<<1]; int n,head[N],cnt,x[N],y[N],deg[N],c[N],mx; pi ans;
inline void addedge(CI x,CI y)
{
	e[++cnt]=(edge){y,head[x]}; head[x]=cnt; ++deg[x];
	e[++cnt]=(edge){x,head[y]}; head[y]=cnt; ++deg[y];
}
#define to e[i].to
inline void DFS(CI now=1,CI fa=0,CI d=1)
{
	c[d]=max(c[d],deg[now]-(fa!=0)); mx=max(mx,d);
	for (RI i=head[now];i;i=e[i].nxt) if (to!=fa) DFS(to,now,d+1);
}
#undef to
int main()
{
	RI i,j; for (scanf("%d",&n),i=1;i<n;++i)
	scanf("%d%d",&x[i],&y[i]),addedge(x[i],y[i]); ans=mp(n+1,0);
	for (i=1;i<=n;++i)
	{
		for (j=1;j<=n;++j) c[j]=0; mx=0; DFS(i);
		long long ret=1; for (j=1;j<mx;++j) ret*=c[j]; ans=min(ans,mp(mx,ret));
	}
	for (i=1;i<n;++i)
	{
		for (j=1;j<=n;++j) c[j]=0; mx=0; DFS(x[i],y[i]); DFS(y[i],x[i]);
		long long ret=1; for (j=1;j<mx;++j) ret*=c[j]; ans=min(ans,mp(mx,ret<<1LL));
	}
	return printf("%d %lld",ans.first,ans.second),0;
}

E - Sequence Growing Hard

完蛋了我连从小到大放来保证字典序的做法都没想到,菜得飞起

考虑到\(A_i\)\(A_{i-1}\)就多了一个元素,我们每次只要保证新加入的元素放在一个不大于它的元素前面即可

但是这样显然会算重,因此我们强制新加入的元素放在一个小于它的元素前面

我们假设\(A_0=0\),这样显然不会影响答案

我们考虑在第\(i\)次操作时,如果我们把新加入的元素放在第\(j\)次操作加入的元素的前面,那么我们可以认为\(j\)\(i\)的父亲

这样进行了\(n\)次操作后,我们就可以得到一个编号为\([0,n]\)\(n+1\)个点的树,并且每个树点有一个\([0,k]\)的权值

这棵树根据前面的构造方法满足从标号和权值的角度来看都是一个严格小根堆

并且我们发现这样的一个严格小根堆树与一种操作方案唯一对应

因此我们求出这样的严格小根堆树的个数即可,设\(f_{i,j}\)表示有\(i\)个点,标号为\([0,i)\),根节点权值为\(j\)的严格小根堆树的个数

转移显然可以以\(0\)号点为根节点,并且枚举\(1\)号点的子树大小转移:

\[f_{i,j}=\sum_{v=j+1}^k \sum_{k=1}^{i-1} f_{i-k,j}\times f_{k,v}\times C_{i-2}^{k-1} \]

\(suf_{i,j}=\sum_{k=j}^k f_{i,k}\)后缀和优化即可变为\(O(n^2k)\)

#include<cstdio>
#define RI register int
#define CI const int&
const int N=305;
int n,m,mod,C[N][N],f[N][N],suf[N][N];
int main()
{
	RI i,j,k; scanf("%d%d%d",&n,&m,&mod);
	for (i=0;i<=n+1;++i) for (C[i][0]=j=1;j<=i;++j)
	C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
	for (i=0;i<=m;++i) f[1][i]=1;
	for (i=m;~i;--i) suf[1][i]=(suf[1][i+1]+f[1][i])%mod;
	for (i=2;i<=n+1;++i)  
	{
		for (j=0;j<=m;++j) for (k=1;k<i;++k)
		(f[i][j]+=1LL*f[i-k][j]*suf[k][j+1]%mod*C[i-2][k-1]%mod)%=mod;
		for (j=m;~j;--j) suf[i][j]=(suf[i][j+1]+f[i][j])%mod;
	}
	return printf("%d",f[n+1][0]),0;
}

Postscript

这场感觉自己好水的说

posted @ 2020-10-28 21:13  空気力学の詩  阅读(90)  评论(0编辑  收藏  举报