[APIO2015]巴厘島的雕塑(數位dp)

【題解】

引用ZYF神犇一句話:"顯然位運算的極值問題都應該從高位向低位考慮。優先讓這一位爲0,如果行的話這一位就是0,否則就設爲1。" 


設答案爲ans,從高位到低位枚舉 是否有使ans的這一位爲0的方案,注意到每一位是互相獨立的 

假設枚舉到了倒數第x位,
即ans的最高位到倒數第x+1位的最優01分佈已確定,現在正在判斷第x位是否有可能填0:
對於每個x,考慮遞推法:
設 布爾數組 f[i][j]表示:將前i個數分j段,能否在 得到的優美度的最高位到倒數第x+1位 都與ans一致的情況下,讓倒數第x位爲0
因爲段與段之間的合併爲or運算,所以遞推方式爲:
若 將前k個數(k<i)分j-1段,得到的優美度本身與ans一致,並且第j段的優美度也與ans一致,纔可以由f[k][j-1]轉移到f[i][j]
如何判斷以上兩個條件:
1. 得到的優美度的最高位到倒數第k+1位 是否與ans一致:( (S[i]-S[k])>>k | ans ) == ans (ans爲0的位,S[i]-S[k]都不爲1)
2. 得到的優美度的倒數第k位能否爲0:( (S[i]-S[k]) & 1<<(k-1) ) == 0

對於每個x,若f[n][A~B]有至少一個爲1,nas的第k位就可以爲0

複雜度:O( logY * n^3 )

對於最後一組數據:A==1,B<=n,段數只有上限 
我們要想把f數組的第二個維度省掉的話,用f[i]記錄將前i個數分段並得到可行解的最小段數,最後判斷其是否小於B,即可 

複雜度:O( logY * n^2 )


注意:第一次WA37是因爲“1<<(LL)x-1LL”,這裏第一個1沒有寫成1LL


【代碼】

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define INF 100000
typedef long long LL;
LL s[2005];
int f[105][105],g[2005];
LL ans=0,t;
int n,A,B,len=0;
int min(int a,int b)
{
	if(a<b) return a;
	return b;
}
void work1()
{
	int x,i,j;
	for(x=len;x>0;x--)
	{
		for(i=1;i<=n;i++)
			g[i]=INF;//g[0]=0;
		for(i=1;i<=n;i++)
			for(j=0;j<i;j++)
				if(g[j]<B)
				{
					t=s[i]-s[j];
					if( (t>>(LL)x|ans)==ans && (t&1LL<<(LL)x-1LL)==0 ) g[i]=min(g[i],g[j]+1);
				}
		ans<<=1LL;
		if(g[n]>B) ans++;
	}
}
void work2()
{
	int x,i,j,k;
	for(x=len;x>0;x--)
	{
		memset(f,0,sizeof(f));
		f[0][0]=1;
		for(i=1;i<=n;i++)
			for(j=1;j<=i;j++)
				for(k=0;k<i;k++)
					if(f[k][j-1])
					{
						t=s[i]-s[k];
						if( (t>>(LL)x|ans)==ans && (t&1LL<<(LL)x-1LL)==0 ) f[i][j]=1;
					}
		for(i=A;i<=B;i++)
			if(f[n][i]) break;
		ans<<=1;
		if(i>B) ans++;
	}
}
int main()
{
	int i;
	scanf("%d%d%d",&n,&A,&B);
	for(i=1;i<=n;i++)
	{
		scanf("%lld",&s[i]);
		s[i]+=s[i-1];
	}
	for(t=s[n];t>0;t>>=1)
		len++;
	if(A==1) work1();
	else work2();
	printf("%lld",ans);
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章