AtCoder Regular Contest 103

Contest Link Official Editorial

C - ////

定义一个序列为 /\/\/\/ ,当且仅当满足:

  • \(\forall i=1\sim n-2,a_i=a_{i+2}\)
  • 序列中有恰好两个不同的数

保证给出的序列长度为偶数。求将给出的长度为 \(n\) 的序列变成 /\/\/\/ 序列的最小替换次数。\(2\leq n\leq 1e5,1\leq v_i\leq 1e5\) .

Solution

简单题。直接桶排一下,然后扫一遍得到奇数位置和偶数位置最大、第二大的众数,如果最大的两个不同,那么直接输出 \(n\) 减去这两个;否则,在奇数最大+偶数第二大和奇数第二大+偶数最大里取一个最大值即可。

//Author:RingweEH
const int N=1e5+10;
int n,cnt_even[N],cnt_odd[N];
int a[N],b[N];

int main()
{
	n=read();
	for ( int i=1; i<=n; i++ )
		if ( i&1 ) cnt_odd[read()]++;
		else cnt_even[read()]++;

	int emx_1=0,emx_2=0;
	for ( int i=1; i<=100000; i++ )
		if ( cnt_even[i]>cnt_even[emx_1] ) emx_2=emx_1,emx_1=i;
		else if ( cnt_even[emx_2]<cnt_even[i] ) emx_2=i;
	int omx_1=0,omx_2=0;
	for ( int i=1; i<=100000; i++ )
		if ( cnt_odd[i]>cnt_odd[omx_1] ) omx_2=omx_1,omx_1=i;
		else if ( cnt_odd[omx_2]<cnt_odd[i] ) omx_2=i;

	if ( omx_1^emx_1 ) { printf( "%d\n",n-cnt_odd[omx_1]-cnt_even[emx_1] ); return 0; }
	omx_2=cnt_odd[omx_2]; omx_1=cnt_odd[omx_1];
	emx_1=cnt_even[emx_1]; emx_2=cnt_even[emx_2];
	int now=max( omx_2+emx_1,omx_1+emx_2 );
	printf( "%d\n",n-now );

	return 0;
}

D - Robot Arms

给定 \(n\) 个点,要求构造出一个序列 \(d\) ,并对每个点构造方案:

  • 使用 LRUD 给出一个长度为 \(m-1\) 的操作序列,使得第 \(i\) 个操作执行 \(d[i]\) 次后,能到达这个点
  • L=Left R=Right U=Up D=Down

Solution

Orz xiaolilsq

显然,如果 \(x_i+y_i\) 的奇偶性不同,肯定无解。而偶数的情况显然能通过奇数得到,所以考虑如何凑出所有的奇数。

用二进制来构造,通过 \(1,2,\cdots ,2^k\) 能得到所有 \(|x|+|y|<2^{k+1}\)\(x+y\) 为奇数的位置。

\(k=0\) 时,显然满足;

\(1\sim 2^{k-1}\) 满足,不妨令 \(|x|>|y|\) ,那么 \(x,y\) 至多只有一个数的第 \(k\) 位为 \(1\) ,如果 \(|x|>2^k\) ,那么令 \(|x|-=2^k\) ,否则 \(|x|=2^k-|x|\) ,仍然满足。

递归求解即可。我因为文件头TLE了两发,因为写错了LRUD WA了若干发,判无解变量没有and又WA了两发

//Author:RingweEH
const int N=1010,M=35;
struct Node
{
	ll x,y;
}a[N];
int n;
ll powe[M];

bool Pd( ll x,ll y,int k )
{
	return abs(x)+abs(y)<powe[k+1];
}

void Work( ll x,ll y,int lim )
{
	if ( lim>=0 )
	{
		if ( abs(x)>abs(y) )
		{
			if ( Pd(x-powe[lim],y,lim-1) ) { Work(x-powe[lim],y,lim-1); printf( "R" ); }
			else { Work(x+powe[lim],y,lim-1); printf( "L" ); }
		}
		else
		{
			if ( Pd(x,y-powe[lim],lim-1) ) { Work(x,y-powe[lim],lim-1); printf( "U" ); }
			else { Work(x,y+powe[lim],lim-1); printf( "D" ); }
		}
	}
}

int main()
{
	powe[0]=1;
	for ( int i=1; i<M; i++ )
		powe[i]=powe[i-1]<<1;

	n=read(); bool fl=1;
	for ( int i=1; i<=n; i++ )
	{
		a[i].x=read(),a[i].y=read();
		if ( i>1 ) fl&=(((a[i].x+a[i].y)&1)==((a[i-1].x+a[i-1].y)&1));
	}
	
	if ( !fl ) { printf( "-1" ); return 0; }
	if ( (a[1].x+a[1].y)&1 )
	{
		printf( "%d\n",M );
		for ( int i=0; i<M; i++ )
			printf( "%lld ",powe[i] );
		printf( "\n" );
	}
	else
	{
		printf( "%d\n",M+1 );
		printf( "1 " );
		for ( int i=0; i<M; i++ )
			printf( "%lld ",powe[i] );
		printf( "\n" );
	}
	for ( int i=1; i<=n; i++ )
	{
		if ( (a[i].x+a[i].y)%2==0 ) 
		{
			printf( "L" ),a[i].x++;
		}
		Work( a[i].x,a[i].y,M-1 ); 
		printf( "\n" );
	}

	return 0;
}

E - Tr/ee

给定一个长度为 \(n\) 的序列 \(s\) ,构造一棵树满足如下条件:

  • 点从 \(1\sim n\) 标号,边从 \(1\sim n-1\) 标号;
  • 如果 \(s[i]=1\) ,那么能通过删除一条边得到一个大小为 \(i\) 的连通块;
  • 如果 \(s[i]=0\) ,那么不能通过删除一条边得到一个大小为 \(i\) 的连通块。

\(2\leq n\leq 1e5\) ,无解输出 \(-1\) .

Solution

首先考虑无解:

  1. \(s[1]=0\) 或者 \(s[n]=1\) ,显然不行
  2. \(s[i]=1\)\(s[n-i]=0\) ,如果能分出一个那么一定也能分出另一个。

然后就可以愉快构造了,还是比较简单的。

设当前加到了第 \(i\) 条边,那么当前连通块里有 \(i+1\) 个点。如果 \(s[i]=1\) ,那么也就是说加这条边之前的那个大小为 \(i\) 的连通块要能独立出来,那么下一次加边就从新的这个端点加,否则还是在老的端点加。设一个 \(rt\) 来表示即可。

//Author:RingweEH
const int N=1e6+10;
char s[N];

int main()
{
	scanf( "%s",s+1 ); int n=strlen(s+1);

	if ( s[1]=='0' || s[n]=='1' ) { printf( "-1" ); return 0; }
	for ( int i=1; i<=n; i++ )
		if ( s[i]=='1' && s[n-i]=='0' ) { printf( "-1" ); return 0; }
	int rt=1;
	for ( int i=2; i<=n; i++ )
	{
		printf( "%d %d\n",rt,i );
		if ( s[i-1]=='1' ) rt=i;
	}

	return 0;
}

F - Distance Sums

给定一个长度为 \(n\) 的序列 \(D\) ,构造满足以下条件的一棵树:

  • 点标号为 \(1\sim n\) ,边标号为 \(1\sim n-1\)
  • 对于每个点 \(i\) ,它到所有点的距离之和为 \(D_i\) (边长为 \(1\)

\(2\leq n\leq 1e5,1\leq D_i\leq 1e12\) .

Solution

显然,\(D_i\) 最大的一个点一定是叶子节点,最小的一定是重心。那么可以从叶子节点开始倒推上去。

对于一个点 \(u\) ,设一个子节点为 \(v\) ,那么显然,\(D_u=D_v+2\cdot siz[v]-n\) 。也就是说,我们可以通过一个 \(D\) 较大的 \(v\) 反推出其父亲 \(u\) ,这样就能从一个叶子节点开始,从大到小遍历 \(D_i\) ,然后一个一个找父亲,更新 \(siz\) 即可。

注意到这样只满足了差值关系,所以最后还要 DFS 验证一遍。

//Author:RingweEH
#define PII pair<int,int>
#define mp make_pair
#define pb push_back
const int N=1e5+10;
int n,siz[N],dep[N];
pair<ll,int> a[N];
vector<int> g[N];
vector<PII> ans;

void Dfs( int u,int fa )
{
	dep[u]=dep[fa]+1;
	for ( int v : g[u] )
		if ( v^fa ) Dfs( v,u );
}

int main() 
{
    n=read();
    for ( int i=1; i<=n; i++ )
    	a[i]=mp(read(),i);

    sort( a+1,a+1+n );
    for ( int i=1; i<=n; i++ )
    	siz[i]=1;
    for ( int i=n; i>1; i-- )
    {
    	ll del=2*siz[a[i].second]-n+a[i].first;
    	int pos=lower_bound( a+1,a+1+n,mp(del,0) )-a;
    	if ( a[pos].first^del ) { printf( "-1" ); return 0; }
    	int u=a[i].second,v=a[pos].second;
    	ans.pb( mp(u,v) );
    	g[u].pb(v); g[v].pb(u); siz[v]+=siz[u];
    }

    dep[0]=-1; 
    Dfs( a[1].second,0 ); ll sum=0;
    for ( int i=1; i<=n; i++ )
    	sum+=dep[i];
    if ( sum!=a[1].first ) { printf( "-1" ); return 0; }
    for ( PII i : ans ) 
    	printf( "%d %d\n",i.first,i.second );

    return 0;
}
posted @ 2021-01-05 10:18  MontesquieuE  阅读(81)  评论(0)    收藏  举报