Atcoder Beginner Contest 327 解题报告

Atcoder Beginner Contest 327

Hints

D $\quad$这个定义……看起来这么熟悉?
E $\quad$固定 $k$ 试试?
F_1 $\quad$扫描线?
F_2 $\quad$区间加,区间 $\max$,咋维护?

A

直接查找 \(\texttt{ab}\)\(\texttt{ba}\) 即可。

int n;
string s;
void Solve(int CASE)
{
	cin>>n>>s;
	puts((~s.find("ab"))||(~s.find("ba"))?"Yes":"No");
}

B

\(16^{16}>10^{18}\implies a\le15\)

ll n;
void Solve(int CASE)
{
	cin>>n;
	for(int i=1;i<=15;i++)
	{
		ll pw=1;
		for(int j=1;j<=i;j++)pw*=i;
		if(pw==n)put_ret(i);//put_ret 就是直接输出然后返回
	}
	cout<<-1;
}

C

玩过数独吗?

判一下每行每列每宫 \(1\sim9\) 是否都出现一次即可。

int c[10][10],r[10][10],g[10][10];
int G(int x,int y){return (x-1)/3*3+(y-1)/3+1;}
void Solve(int CASE)
{
	for(int i=1;i<=9;i++)
		for(int j=1;j<=9;j++)
		{
			int x;cin>>x;
			r[i][x]++,c[j][x]++,g[G(i,j)][x]++;
		}
	for(int i=1;i<=9;i++)
		for(int j=1;j<=9;j++)
			if(c[i][j]!=1||r[i][j]!=1||g[i][j]!=1)put_ret("No");
	cout<<"Yes";
}

D

观察这个 good 的定义,发现其实就是一个 \(n\) 个点的无向图,其中 \(s_i\)\(t_i\) 连边,然后让你判断是否是个二分图。

const int N=200005;
int n,m;
VI g[N];
int col[N],X[N];
bool dfs(int x)
{
	for(int y:g[x])
	{
		if(!~col[y])
		{
			col[y]=!col[x];
			if(!dfs(y))return 0;
		}
		else if(col[y]==col[x])return 0;
	}
	return 1;
}
void Solve(int CASE)
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)cin>>X[i];
	for(int i=1;i<=m;i++)
	{
		int y;cin>>y;
		g[X[i]].PB(y),g[y].PB(X[i]);
	}
	memset(col,-1,sizeof(col));
	for(int i=1;i<=n;i++)
		if(!~col[i])
		{
			if(!dfs(i))put_ret("No");
		}
	cout<<"Yes";
}

E

好像真的挺类似于 atcoder 的 rating 计算。

一看到这么复杂的式子,肯定想 dp。

dp 状态是啥呢,肯定是 \(k\) 固定时某个东西的最大值。

但是这么复杂的式子,感觉没法 dp 啊!

等等,dp 状态是「\(k\) 固定时某个东西的最大值」,并没有说是什么东西。

观察这一坨式子,就能发现其实只有左边分式的分子是与 perf 的具体值有关。

那么考虑 dp 分子,注意到打了某场比赛时

\[\begin{aligned}dp_i&=\max\left\{\sum_{i=1}^k(0.9)^{k-i}Q_i\right\}\\&=\max\left\{\sum_{i=1}^{k-1}\left((0.9)^{k-i}Q_i\right)+Q_k\right\}\\&=\max\left\{0.9\sum_{i=1}^{k-1}\left((0.9)^{k-i-1}Q_i\right)+Q_k\right\}\\&=\max\{0.9dp_{k-1}+Q_k\}\end{aligned}, \]

所以这东西就能转移了。最后枚举 \(k\) 计算一下最终 rating 然后取个最大值即可。

时间复杂度 \(\Theta(n^2)\)

const int N=5005;
int n,a[N];
double dp[N];
void Solve(int CASE)
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		for(int j=i;j;j--)
			dp[j]=max(dp[j],dp[j-1]*0.9+a[i]);
	}
	double ans=-1e100,sum=0,pw=1;
	for(int i=1;i<=n;i++)
	{
		sum+=pw,pw*=0.9;
		ans=max(ans,dp[i]/sum-1200/sqrt(i));
	}
	Fix(20)<<ans;
}

F

扫描线,假设第一维扫描到 \(x\),则第一维覆盖的范围是 \([x,x+D)\)。下面考虑第二维。

那么就是维护 \(f(y)=[y,y+W)\) 的区间内总点数,(\(f(y)\))的最大值。

注意到扫描线移动一格时,其实就是加了一些点,删了一些点。

注意到加入点 \(a\) 时要将 \((a-W,a]\) 内点的 \(f\) 值加一,删除时就是减一。

区间加整体 \(\max\),一个线段树搞定。时间复杂度 \(\Theta(n\log n)\)

const int N=200005,S=N-5;
int n,X,Y,x[N],y[N];
VI pos[N];
struct tree
{
	struct node
	{
		int lz,mx;
	}tr[N<<2];
#define mid (l+r>>1)
#define rmd (mid+1)
#define lid (id<<1)
#define rid (lid|1)
	void pushup(int id)
	{
		tr[id].mx=max(tr[lid].mx,tr[rid].mx);
	}
	void pushdown(int id)
	{
		int &l=tr[id].lz;
		tr[lid].lz+=l,tr[rid].lz+=l,tr[lid].mx+=l,tr[rid].mx+=l;
		l=0;
	}
	void build(int l=1,int r=S-Y+1,int id=1)//size = S - Y + 1
	{
		tr[id].lz=tr[id].mx=0;
		if(l==r)return;
		build(l,mid,lid);
		build(rmd,r,rid);
	}
	void modify(int ql,int qr,int v,int l=1,int r=S-Y+1,int id=1)
	{
		if(ql<=l&&qr>=r){tr[id].mx+=v,tr[id].lz+=v;return;}
		pushdown(id);
		if(ql<=mid)modify(ql,min(qr,mid),v,l,mid,lid);
		if(qr>=rmd)modify(max(ql,rmd),qr,v,rmd,r,rid);
		pushup(id);
	}
	int query(){return tr[1].mx;}
}t;
void Solve(int CASE)
{
	cin>>n>>X>>Y;
	for(int i=1;i<=n;i++)cin>>x[i]>>y[i],pos[x[i]].PB(y[i]);
	t.build();
	for(int i=1;i<X;i++)
		for(int j:pos[i])t.modify(max(j-Y+1,1),min(j,S-Y+1),1);
	int ans=0;
	for(int i=1;i<=S-X+1;i++)
	{
		for(int j:pos[i+X-1])t.modify(max(j-Y+1,1),min(j,S-Y+1),1);
		for(int j:pos[i-1])t.modify(max(j-Y+1,1),min(j,S-Y+1),-1);
		ans=max(ans,t.query());
	}
	cout<<ans;
}

G

神仙容斥题。具体自己看题解

题解式子有点问题。建议理解后自己推一遍,或者看这里的更正

const int N=31,S=30;
MI C[N*N][N*N];
void Init()
{
	C[0][0]=1;
	for(int i=1;i<=S*S;i++)
		for(int j=0;j<=i;j++)
			C[i][j]=C[i-1][j]+(j?C[i-1][j-1]:0);
}
int n,m;
MI b(int x,int y)
{
	MI ans=0;
	for(int i=1;i<=y;i++)
		ans+=MI((i^y)&1?-1:1)*C[y][i]*pw<MI>(i,x);
	return ans;
}
MI f[N][N*N],g[N][N*N],h[N][N*N];
int E(int x){return (x>>1)*(x+1>>1);}
void Solve(int CASE)
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		for(int j=0;j<=E(i);j++)
		{
			for(int k=0;k<=i;k++)
				g[i][j]+=C[i][k]*C[k*(i-k)][j];
		}
	for(int i=1;i<=n;i++)
		for(int j=0;j<=E(i);j++)
		{
			h[i][j]=g[i][j];
			for(int k=1;k<i;k++)
				for(int l=0;l<=j;l++)
					h[i][j]-=C[i-1][k-1]*h[k][l]*g[i-k][j-l];
		}
	const int i2=499122177;
	for(int i=1;i<=n;i++)
		for(int j=0;j<=E(i);j++)
		{
			f[i][j]=h[i][j]*i2;
			for(int k=1;k<i;k++)
				for(int l=0;l<=j;l++)
					f[i][j]+=C[i-1][k-1]*h[k][l]*f[i-k][j-l]*i2;
		}
	MI ans=0;
	for(int i=0;i<=E(n);i++)
		ans+=b(m,i)*f[n][i];
	ans*=pw(2_M,m);
	cout<<ans;
}
posted @ 2023-11-04 22:08  No_Play_Yes_Splay  阅读(118)  评论(0编辑  收藏  举报