Atcoder Grand Contest 011&012

012E Camel and Oases

题目描述

点此看题

解法

考试时直接切了,不知道这题有什么难的,我都会做的题肯定是水题

首先有一个问题转化:我们可以将原序列划分为 \(\log\) 个连续段,使得每一段的 \(\max(x_i-x_{i-1})\leq v_j\),其中 \(v_j=\frac{k}{2^j}\) 表示第 \(j\) 次跳跃后背包的容量(注意 \(v_j=0\) 也是合法的,但是只能存在一次)

首先考虑全局存不存在合法方案,一个重要的观察是只要我们确定 \(v\)分配顺序,那么可以通过贪心来确定段的划分,就是按照这个顺序能取就取。解决顺序问题可以考虑状压 \(dp\),设 \(dp[s]\) 表示已使用的 \(v\) 集合为 \(s\) 的最远延伸距离。

那么怎么确定单个位置的答案呢?一个简单的观察是:\(x_i-x_{i-1}>k\) 的最多只存在 \(\log k\) 对,要不然就全局无解。所以我们以 \(x_i-x_{i-1}>k\) 为断点,由于每个点为起点都会取遍段的位置之后再离开,段中每个位置的答案是一样的

设段是 \([l,r]\),设 \(f(s),g(s)\) 分别表示前缀 \(/\) 后缀的最远延伸位置,那么可以枚举集合 \(s\),设补集是 \(t\),判断条件就是:

\[f(s)\geq l-1\and g(t)\leq r+1 \]

都可以暴力做,时间复杂度 \(O(n\log n)\)

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 1<<21;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,t,v[30],p[30],l[22][M],r[22][M],x[M],f[M],g[M];
signed main()
{
	n=read();m=read();
	for(int x=m;x;x/=2) v[k++]=x;v[k++]=0;
	for(int i=1;i<=n;i++) x[i]=read();
	for(int w=0;w<k;w++)
	{
		l[w][0]=l[w][1]=1;
		r[w][n]=r[w][n+1]=n;
		for(int i=2;i<=n;i++)
		{
			if(x[i]-x[i-1]<=v[w])
				l[w][i]=l[w][i-1];
			else l[w][i]=i;
		}
		for(int i=n-1;i>=1;i--)
		{
			if(x[i+1]-x[i]<=v[w])
				r[w][i]=r[w][i+1];
			else r[w][i]=i;
		}
	}
	memset(g,0x3f,sizeof g);g[0]=n+1;
	for(int s=0;s<(1<<k);s++)
		for(int i=0;i<k;i++) if(!(s>>i&1))
		{
			f[s|(1<<i)]=max(f[s|(1<<i)],r[i][f[s]+1]);
			g[s|(1<<i)]=min(g[s|(1<<i)],l[i][g[s]-1]);
		}
	for(int i=2;i<=n;i++)
		if(x[i]-x[i-1]>m) p[++t]=i;
	if(t>k)
	{
		for(int i=1;i<=n;i++) puts("Impossible");
		return 0; 
	}
	for(int x=1,y;x<=n;x=y+1)
	{
		y=r[0][x];int fl=0,t=(1<<k)-1-1;
		for(int s=0;s<(1<<k);s++) if(!(s&1))
			fl|=(f[s]>=x-1 && g[s^t]<=y+1);
		for(int i=x;i<=y;i++)
			puts(fl?"Possible":"Impossible");
	}
}

011D Half Reflector

题目描述

点此看题

解法

首先要知道单次操作之后会有什么样的变化,如果首位是 A 那么会立马弹开,如果首位是 B,那么经过它之后会变成 A,我们继续考虑后续的情况,关键的观察是球的上一个位置一定是 A

  • 下一个位置是 B,那么对于 AB 会变成 AA
  • 下一个位置是 A,那么对于 AA 会变成 BA

使用归纳法,我们可以发现球的作用相当于给每个位置左移之后取反(在环的意义下,正好最后一个位置一定是 A

那么就可以通过打整体标记实现 \(O(k)\) 模拟了。考试时我观察数据发现后缀会趋向 ABABAB... 这种类型,而且在 \(2n\) 次之内就会稳定下来,但是我们需要保留原来 \(k\) 的奇偶性,这样我们就可以在 \(O(n)\) 的时间内模拟了。

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 200005; 
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,k,a[M];char s[M];
signed main()
{
	n=read();k=read();scanf("%s",s+1);
	for(int i=1;i<=n;i++) a[i]=s[i]-'A';
	int t=0,p=1;k=min(k,2*n+(k&1));
	while(k--)
	{
		if(t^a[p]) t^=1,p=p%n+1;
		else a[p]^=1;
	}
	for(int i=p;i<=n;i++) putchar((a[i]^t)?'B':'A');
	for(int i=1;i<p;i++) putchar((a[i]^t)?'B':'A');
}

012F Prefix Median

题目描述

点此看题

解法

直接按照生成方式去规划 \(b\) 序列是困难的,我们需要先寻找一些 \(b\) 序列的性质。我们可以感受到,\(b\) 序列的每个元素都有其大致的范围,并且 \(b\) 序列的变化是连续的,那么写出必要条件:

  • \(b_i\in\{a_1,a_2...a_{2n-1}\}\),显然的必要性。
  • \(a_i\leq b_i\leq a_{2n-i}\),容易发现不在这个范围内一定无法成为中位数。
  • 对于 \(i\)\(i+1\),不存在 \(j\leq i\) 使得 \(p_i<p_j<p_{i+1}\) 或者 \(p_i>p_j>p_{i+1}\),因为中位数只能移动一个位置,这里要加 \(j\leq i\) 的原因就是只考虑前面出现过的数(所以这部分 soulist 讲错了)

那么用上面的必要条件来构造即可证明充分性。考虑根据 \(p_{i+1}\)\(p_i\) 的大小关系来构造,如果 \(p_i=p_{i+1}\) 那么添加剩下的最大值和最小值;如果 \(p_i>p_{i+1}\),并且如果 \(p_{i+1}\) 是第一次出现,那么添加 \(p_{i+1}\) 和最小值,否则添加最小值和次小值;如果 \(p_i<p_{i+1}\),并且如果 \(p_{i+1}\) 是第一次出现,那么添加 \(p_{i+1}\) 和最大值,否则添加最大值和次大值。这样构造一定有解。

那么用这个等价条件去规划 \(b\) 序列即可,首先我们把 \(a_i\) 排序,观察到 \(b_n=a_n\),那么我们以 \(n\) 为起始点往回推。由于 \(b\) 取值范围的变化我们需要加入 \(a_{i}\)\(a_{2n-i}\),我们已知 \(p_{i+1}\) 现在要确定 \(p_i\),确定之后我们需要删除 \((p_i,p_{i+1})\) 中的所有数。

\(x=p_i\),那么我们记录 \(x\) 左边有多少数,\(x\) 右边有多少数即可,所以设 \(dp[i][l][r]\) 表示考虑到 \(p_i\)\(x\) 左边有 \(l\) 个数,\(x\) 右边有 \(r\) 个数的方案数。转移枚举 \(x'\) 即可,注意相同的数加入的时候需要当成一个数。

时间复杂度 \(O(n^4)\)

#include <cstdio>
#include <algorithm>
using namespace std;
const int M = 105;
const int MOD = 1e9+7;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,ans,a[M],dp[M][M][M];
void add(int &x,int y) {x=(x+y)%MOD;}
signed main()
{
	n=read();m=2*n;
	for(int i=1;i<m;i++) a[i]=read();
	sort(a+1,a+m);
	dp[n][0][0]=1;
	for(int i=n-1;i>=1;i--)
	{
		int x=a[i]!=a[i+1],y=a[m-i]!=a[m-i-1];
		for(int l=0;l<m;l++) for(int r=0;r<m;r++)
		{
			int t=dp[i+1][l][r];if(!t) continue;
			add(dp[i][l+x][r+y],t);
			for(int d=0;d<l+x;d++)
				add(dp[i][d][r+y+1],t);
			for(int d=0;d<r+y;d++)
				add(dp[i][l+x+1][d],t); 
		}
	}
	for(int l=0;l<m;l++) for(int r=0;r<m;r++)
		add(ans,dp[1][l][r]);
	printf("%d\n",ans);
}
posted @ 2022-02-22 15:37  C202044zxy  阅读(86)  评论(0编辑  收藏  举报