LOJ#2886. 「APIO2015」巴厘岛的雕塑 Bali Sculptures

15 篇文章 0 订阅
4 篇文章 0 订阅

题意:

巴厘岛的一条主干道上共有 N N N 座雕塑,依次编号为 1 1 1 N N N。雕塑 i i i 的年龄为 Y i Y_i Yi
政府想把这些雕塑分成恰好 X X X 组,要求 A ≤ X ≤ B A≤X≤B AXB。每组不能为空,且每组雕塑的编号必须连续。每个雕塑必须属于某一组。
分组方案需要考虑美观程度。计算方法如下:分别计算每组雕塑的年龄之和,然后将每一组的结果按位取或,就得到了该分组方案的美观值。
求最小的美观值。

数据范围:

前四档: N &lt; = 100 , 1 &lt; = A , B &lt; = N , Y i &lt; = 1 0 9 N&lt;=100,1&lt;=A,B&lt;=N,Y_i&lt;=10^9 N<=100,1<=A,B<=N,Yi<=109
最后一档: N &lt; = 2000 , A = 1 , B &lt; = N , Y i &lt; = 1 0 9 N&lt;=2000,A=1,B&lt;=N,Y_i&lt;=10^9 N<=2000,A=1,B<=N,Yi<=109

Analysis:

这数据范围很显然要分段了。。。
先考虑前四档怎么做,直接DP肯定不行,贪心逐位确定?
考虑一位能不能填0,那么要求分出来的组之和这一位都不能有1,那么前面的位怎么办?
发现只要满足前面的位不出现多余的1即可,因为我们贪心已经使前面最小了。
那么一个 N 3 log ⁡ ( N ∗ Y i ) N^3\log{(N*Y_i)} N3log(NYi)的DP就做完了。
考虑第五档部分分怎么做,发现只要让分的组尽量少,那么状态改设 f i f_i fi表示分到 i i i且合法的最小组数,像上面一样转移即可。复杂度 O ( N 2 ∗ log ⁡ ( N ∗ Y i ) ) O(N^2*\log{(N*Y_i)}) O(N2log(NYi))

Code:

# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
const int N = 1e2 + 5;
const int M = 2e3 + 5;
typedef long long ll;
int a[M],f[N][N],g[M];
int n,A,B;
int main()
{
	scanf("%d%d%d",&n,&A,&B);
	for (int i = 1 ; i <= n ; ++i) scanf("%d",&a[i]);
	if (n <= 100)
	{
		ll ret = 0;
		for (int i = 40 ; ~i ; --i)
		{
			memset(f,0,sizeof(f));
			f[0][0] = 1;
			for (int j = 1 ; j <= n ; ++j)
			{
				ll su = 0;
				for (int k = j ; k ; --k)
				{
					su += a[k];
					for (int l = 0 ; l < j ; ++l)
					if (f[k - 1][l] && !(su & (1ll << i)) && (((su >> i) << i) | ret) == ret) f[j][l + 1] = 1;
				}
			}
			bool flag = 0;
			for (int j = A ; j <= B ; ++j)
			if (f[n][j]) { flag = 1; break; }
			if (!flag) ret |= 1ll << i;
		}
		printf("%lld\n",ret);
	}else
	{
		ll ret = 0;
		for (int i = 40 ; ~i ; --i)
		{
			memset(g,0x3f,sizeof(g));
			g[0] = 0;
			for (int j = 1 ; j <= n ; ++j)
			{
				ll su = 0;
				for (int k = j ; k ; --k)
				{
					su += a[k];
					if (!(su & (1ll << i)) && (((su >> i) << i) | ret) == ret) g[j] = min(g[j],g[k - 1] + 1);
				}
			}
			if (g[n] > B) ret |= 1ll << i;
		}
		printf("%lld\n",ret);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值