Live2D

Solution -「多校联训」假人

Description

  Link.

  一种物品有 长度权值 两种属性,现给定 n 组物品,第 i 组有 ki 个,分别为 (1,ai,1)..(ki,ai,ki),求在每组物品里恰好选择一个物品,且物品长度和恰为 i=n..k 时的最大物品权值和。

  n105ki5

Solution

  本次 NOIP 模拟赛 考察的知识点包括但不限于:凸性函数的研究应用、闵可夫斯基和、Gale-Ryser 定理、LCT……是一套非常优秀的组题。

  先令所有长度 1,长度区间变为 [0,K=4]。考虑暴力 DP,令 f(i,j) 在前 i 组中选出长度和为 j 的物品时最大权值和。

  结论:L=12fi,r(x)=f(i,xL+r) (r[0,L),xN),则 fi,r(x) 的图像是上凸的点集。

证明   承认这样一个结论:对于整数集 AaA,a[0,4],aA=24,则 BA,bBb=12

  作者水平有限,没有找到除暴搜和冗长分类讨论之外的简洁证明。且满足这一性质的常数值除 ([0,4],24) 以外,还有许多分布规律不明显的解。有兴趣的读者欢迎一起讨论。

  此后,考虑任意 fi,r(x1),fi,r(x),fi,r(x+1)。研究从 fi,r(x1) 的最优解调整得到 fi,r(x+1) 最优解的过程,设第 j 组物品所选长度变化量为 Δj,可以看出 Δj[44],且 Δj=2L=24。我们能通过贪心的方式构造将 Δj 分组,使得每组 Δj 之和 [0,4],运用上文结论,得到两组长度变化量为 12 的调整方案 D1,D2。将其中权值变化量较大的一组作用在 fi,r(x1) 上可以得到 fi,r(x) 的一个下界,即有 fi,r(x)fi,r(x1)+fi,r(x+1)2,结合 x=(x1)+(x+1)2,我们证明了两点中点在图像形成的多边形内,即,图像是(上)凸的。

  利用结论,分治 + 闵可夫斯基和求出函数 fn,0..L1 即可。复杂度 O(LKnlogn)

Code

  由于着急补题,直接套了计算几何的板子。针对凸性 DP 的高效归并可以看 OneInDark 的博客

/*~Rainybunny~*/

#ifndef RYBY
#pragma GCC optimize( "Ofast" )
#endif

#include <bits/stdc++.h>

#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;

inline char fgc() {
	static char buf[1 << 17], *p = buf, *q = buf;
	return p == q && ( q = buf + fread( p = buf, 1, 1 << 17, stdin ), p == q )
	  ? EOF : *p++;
}

inline int rint() {
	int x = 0, s = fgc();
	for ( ; s < '0' || '9' < s; s = fgc() );
	for ( ; '0' <= s && s <= '9'; s = fgc() ) x = x * 10 + ( s ^ '0' );
	return x;
}

inline void wint( const LL x ) {
	if ( 9 < x ) wint( x / 10 );
	putchar( x % 10 ^ '0' );
}

inline void chkmax( LL& u, const LL v ) { u < v && ( u = v ); }

const int MAXN = 1e5;

namespace PGP {

const double EPS = 1e-9, PI = acos( -1. );

inline int dcmp( const double a ) {
    return -EPS < a && a < EPS ? 0 : a < 0 ? -1 : 1;
}

struct Point {
    int x; LL y;
    Point(): x( 0 ), y( 0 ) {}
    Point( const int a, const LL b ): x( a ), y( b ) {}
    inline Point operator + ( const Point& p ) const {
        return { x + p.x, y + p.y };
    }
    inline Point operator - ( const Point& p ) const {
        return { x - p.x, y - p.y };
    }
    inline LL operator ^ ( const Point& p ) const {
        return x * p.y - y * p.x;
    }
    inline double angle() const {
		double t = atan2( y, x );
		return t < 0 ? t + 2 * PI : t;
	}
};

typedef Point Vector;
typedef std::vector<Point> Convex;

inline Convex minkowskiSum( const Point& ap, const Point& bp,
  const Convex& A, const Convex& B ) {
    int n = int( A.size() ), m = int( B.size() );
    static Convex ret; ret.clear(), ret.resize( n + m + 1 );
    
    ret[0] = ap + bp;
    int i = 0, j = 0, k = 0;
    while ( i < n && j < m ) {
        ret[k + 1] = ( ret[k] + ( ( A[i] ^ B[j] ) > 0 ? A[i++] : B[j++] ) );
        ++k;
    }
    while ( i < n ) ret[k + 1] = ret[k] + A[i++], ++k;
    while ( j < m ) ret[k + 1] = ret[k] + B[j++], ++k;
    return ret;
}

} using namespace PGP;

struct Atom {
	std::vector<Point> f;
	
	friend inline Atom operator + ( const Atom& u, const Atom& v ) {
		static std::vector<Point> ur[12], vr[12]; static Atom ret;
		static Point up[12], vp[12];
		ret.f.clear(), ret.f.resize( u.f.size() + v.f.size() - 1 );
		rep ( i, 0, int( ret.f.size() ) - 1 ) ret.f[i].x = i;
		rep ( i, 0, 11 ) ur[i].clear(), vr[i].clear();
		
		rep ( i, 0, int( u.f.size() ) - 1 ) ur[i % 12].push_back( u.f[i] );
		rep ( i, 0, int( v.f.size() ) - 1 ) vr[i % 12].push_back( v.f[i] );
		rep ( i, 0, 11 ) {
			std::reverse( ur[i].begin(), ur[i].end() );
			std::reverse( vr[i].begin(), vr[i].end() );
			
			int n = int( ur[i].size() );
			if ( n ) {
				up[i] = ur[i][0];
				rep ( j, 0, n - 2 ) ur[i][j] = ur[i][j + 1] - ur[i][j];
				ur[i][n - 1] = up[i] - ur[i][n - 1];
			}
			
			n = int( vr[i].size() );
			if ( n ) {
				vp[i] = vr[i][0];
				rep ( j, 0, n - 2 ) vr[i][j] = vr[i][j + 1] - vr[i][j];
				vr[i][n - 1] = vp[i] - vr[i][n - 1];
			}
		}
		
		rep ( i, 0, 11 ) if ( !ur[i].empty() ) {
			rep ( j, 0, 11 ) if ( !vr[j].empty() ) {
				const auto&& cvx( minkowskiSum( up[i], vp[j], ur[i], vr[j] ) );
				for ( size_t k = 0; k < cvx.size(); ++k ) {
					chkmax( ret.f[cvx[k].x].y, cvx[k].y );
				}
			}
		}
		return ret;
	}
};

inline Atom solve( const int l, const int r ) {
	if ( l == r ) {
		int k = rint(), v;
		static Atom ret; ret.f.resize( k );
		rep ( i, 1, k ) ret.f[i - 1] = { i - 1, rint() };
		return ret;
	}
	int mid = l + r >> 1;
	const Atom &&u( solve( l, mid ) ), &&v( solve( mid + 1, r ) );
	return u + v;
}

int main() {
	freopen( "fake.in", "r", stdin );
	freopen( "fake.out", "w", stdout );
	
	const Atom&& ans( solve( 1, rint() ) );
	for ( const auto& u: ans.f ) wint( u.y ), putchar( ' ' );
	putchar( '\n' );
	return 0;
}

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