1. 程式人生 > >LOJ#2886. 「APIO2015」巴厘島的雕塑 Bali Sculptures

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

題意:

巴厘島的一條主幹道上共有 N N 座雕塑,依次編號為 1 1 N

N 。雕塑 i i 的年齡為 Y i Y_i
政府想把這些雕塑分成恰好 X
X
組,要求 A X B A≤X≤B 。每組不能為空,且每組雕塑的編號必須連續。每個雕塑必須屬於某一組。
分組方案需要考慮美觀程度。計算方法如下:分別計算每組雕塑的年齡之和,然後將每一組的結果按位取或,就得到了該分組方案的美觀值。
求最小的美觀值。

資料範圍:

前四檔: N < = 100 , 1 < = A , B < = N , Y i < = 1 0 9 N<=100,1<=A,B<=N,Y_i<=10^9
最後一檔: N < = 2000 , A = 1 , B < = N , Y i < = 1 0 9 N<=2000,A=1,B<=N,Y_i<=10^9

Analysis:

這資料範圍很顯然要分段了。。。
先考慮前四檔怎麼做,直接DP肯定不行,貪心逐位確定?
考慮一位能不能填0,那麼要求分出來的組之和這一位都不能有1,那麼前面的位怎麼辦?
發現只要滿足前面的位不出現多餘的1即可,因為我們貪心已經使前面最小了。
那麼一個 N 3 log ( N Y i ) N^3\log{(N*Y_i)} 的DP就做完了。
考慮第五檔部分分怎麼做,發現只要讓分的組儘量少,那麼狀態改設 f i f_i 表示分到 i i 且合法的最小組數,像上面一樣轉移即可。複雜度 O ( N 2 log ( N Y i ) ) O(N^2*\log{(N*Y_i)})

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