Live2D

Solution -「NOI 模拟赛」出题人

Description

  给定 {an},求一个 {bn1},使得 x{an},i,j[1,n),bi+bj=x。输出 {bn1} 以及对于每个 x 所应该取的 i,j,或断言不可能。

  n30

Solution

  原来不是构造题啊。(悲

  若 a 中有偶数 2k,取一个 b=k,然后随便填就好,先特判掉。

  考虑已有了 {bn1},对于某个 bi+bj=ak,在 bibj 之间连接一条权为 ak 的边。显然,这些边最终构成了 n1 个点 n 条边的图,必然存在偶环。不妨设环上的 bb1,b2,,bk,边权为 a1,a2,,ak,则应有

{b1+b2=a1b2+b3=a2bk+b1=ak

可以钦定 b1=0,剩下 k1 个变量和 k 个方程,结合环的形状分析发现有解当且仅当 2iai=2iai。所以用 MiM 在 {an} 中找两个等大等和且不交的子集用于构造即可。

  假设 Hash 的复杂度为 O(1),则最终复杂度为 O(3n2)

Code

  比较卡 qwq。

/* Clearink */

#include <cstdio>
#include <vector>
#include <cassert>
#include <cstdlib>

#define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
#define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i )

typedef long long LL;

const int MAXN = 30;
const LL INF = 1e11;
int n, half, pwr[20], ans[MAXN + 5];
LL a[MAXN + 5], b[MAXN + 5];

static const int M = 100000037, MAXND = 1.5e7;
int node, head[M], val[MAXND], nxt[MAXND];
LL key[MAXND];

inline int& get( const LL k ) {
	int r = head[( k % M + M ) % M], las = -1;
	for ( ; r && key[r] != k; r = nxt[las = r] );
	if ( r ) return val[r];
	if ( !~las ) head[( k % M + M ) % M] = r = ++node;
	else nxt[las] = r = ++node;
	return key[r] = k, val[r] = -1;
}
	
inline bool count( const LL k ) {
	int r = head[( k % M + M ) % M], las = -1;
	for ( ; r && key[r] != k; r = nxt[las = r] );
	return !!r;
}

inline void solve( const int s1, const int s2 ) {
	std::vector<int> pos, neg;
	rep ( i, 1, half ) {
		int f = s1 / pwr[half - i] % 3;
		if ( f == 1 ) pos.push_back( i );
		else if ( f ) neg.push_back( i );
	}
	rep ( i, half + 1, n ) {
		int f = s2 / pwr[n - i] % 3;
		if ( f == 1 ) neg.push_back( i );
		else if ( f ) pos.push_back( i );
	}

	assert( pos.size() == neg.size() );

	int s = pos.size();
	b[1] = 0;
	rep ( i, 2, s << 1 ) {
		if ( !( i & 1 ) ) {
			b[i] = a[pos[( i >> 1 ) - 1]] - b[i - 1];
			ans[pos[( i >> 1 ) - 1]] = i - 1;
		} else {
			b[i] = a[neg[( i >> 1 ) - 1]] - b[i - 1];
			ans[neg[( i >> 1 ) - 1]] = i - 1;
		}
	}
	ans[neg[s - 1]] = s << 1;
	b[0] = s << 1;

	rep ( i, 1, n ) if ( !ans[i] ) {
		b[++b[0]] = a[i], ans[i] = -b[0];
	}

	puts( "Yes" );
	rep ( i, 1, n ) printf( "%lld%c", b[i], i < n ? ' ' : '\n' );
	rep ( i, 1, n ) {
		if ( ans[i] > 0 ) {
			if ( ans[i] < s << 1 ) printf( "%d %d\n", ans[i], ans[i] + 1 );
			else printf( "%d 1\n", s << 1 );
		} else {
			printf( "1 %d\n", -ans[i] );
		}
	}
}

inline void dfs1( const int l, const int r, const int sta, const LL sum ) {
	if ( l > r ) return void( get( sum ) = sta );
	dfs1( l + 1, r, sta * 3, sum );
	dfs1( l + 1, r, sta * 3 + 1, sum + a[l] + INF );
	dfs1( l + 1, r, sta * 3 + 2, sum - a[l] - INF );
}

inline void dfs2( const int l, const int r, const int sta, const LL sum ) {
	if ( l > r ) {
		if ( sum && count( -sum ) ) solve( get( sum ), sta ), exit( 0 );
		return ;
	}
	dfs2( l + 1, r, sta * 3, sum );
	dfs2( l + 1, r, sta * 3 + 1, sum + a[l] + INF );
	dfs2( l + 1, r, sta * 3 + 2, sum - a[l] - INF );
}

int main() {
	freopen( "problemsetter.in", "r", stdin );
	freopen( "problemsetter.out", "w", stdout );

	scanf( "%d", &n );
	rep ( i, 1, n ) scanf( "%lld", &a[i] );

	if ( n == 1 ) {
		if ( a[1] % 2 ) puts( "No" );
		else printf( "Yes\n%lld\n1 1\n", a[1] / 2 );
		return 0;
	}

	rep ( i, 1, n ) if ( !( a[i] % 2 ) ) {
		printf( "Yes\n%lld", a[i] / 2 );
		rep ( j, 1, n ) if ( i != j ) printf( " %lld", a[j] - ( a[i] / 2 ) );
		putchar( '\n' );
		rep ( j, 1, n ) {
			if ( i == j ) puts( "1 1" );
			else printf( "1 %d\n", j + ( j < i ) );
		}
		return 0;
	}
	
	half = n + 1 >> 1, pwr[0] = 1;
	rep ( i, 1, half ) pwr[i] = pwr[i - 1] * 3;

	dfs1( 1, half, 0, 0 );
	dfs2( half + 1, n, 0, 0 );

	puts( "No" );
	return 0;
}

posted @   Rainybunny  阅读(97)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示