題解——[APIO2015]巴厘島的雕塑 貪心+DP+特殊數據優化
阿新 • • 發佈:2018-05-12
ret div 沒有 最大 set ongl 每次 亂搞 api
最後如果在f[n][A ~ B]中有任意一個為true的話,ans就不用修改(因為默認已經是0了)
而如果一個都沒有,那麽就將ans的當前位變成1,
那麽考慮如何判斷,
首先運算符肯定只能用or,不然是會遺漏信息的(也許可以用其他的吧,但是我沒想到)
考慮這樣兩個二進制串:
now :101100 1011
ans : 101100 0000
中間的空格代表把第k位和後面隔開,
通過一些二進制的基本性質(其實也是所有進制的基本性質)可以發現,
如果前面都是相同的話,因為ans的小於k的位數都還不確定,都是0,
所以最大相差也就是2^(k-1),
反之如果前面有沖突,那麽由於沖突在前面,相差一定會超過2^(k-1),
所以直接判斷now和ans的差就可以了?
不是!
因為now是中間過程,所以now是可能小於ans的,
所以如果是這樣的情況的話:
now : 100110 1011
ans : 101100 0000
這樣的話now < ans,所以now - ans 一定小於 2^(k-1),-1是因為第k位對應的是2^(k-1),幾次方是從0開始數的
但是這樣是不合法的,之所以這樣會被誤判為合法,是因為它之前的那個粗體0幫後面的粗體1抵消掉了。
因此為了這樣的0不會對真正不合法的1造成幹擾,
我們只需要計算(now | ans) - ans < 2^(k-1)是否成立即可,
因為這樣的話,所有這樣的0都會變為1,於是這時要再有不合法的1出現就無法被抵消了。
於是小數據就解決了,但是考慮到最大的點n <= 2000,這樣的復雜度肯定是過不去的
但是觀察發現,對於最大的那幾個點,滿足A == 1,
而之前的f數組之所以要開二維,還要枚舉段數,就是因為只有枚舉了所有情況才能保證不漏掉A~B之間的答案,
因為段數小不一定更優,不然可能會找到小於A的答案,
但是因為A == 1,就不可能會找到小於A的答案了,這時也滿足段數小的一定更優,
所以我們直接令g[i]表示dp到i,使第k位為0的最小段數,
然後轉移時判斷條件和上面一樣即可,註意初始化g[0]=0,其余為極大值
下面放代碼:
寫了好久。。。。
剛剛調了一個小時各種對拍,,,,最後發現是多寫了一個等號,,,,內心拒絕
表示一開始看真的是各種懵逼啊
在偷聽到某位大佬說的從高位開始貪心後發現可做
首先考慮小數據(因為可以亂搞)
所以先從高位開始枚舉位數:
for(int k=all; k ;k--)
ans表示當前答案,f[i][j]表示dp到第i為,分為j段,是否可以滿足第k位為0
ans初始為0,每次DP完就修改ans,
所以枚舉i和j,然後枚舉上一段的結尾l,
由於是or,所以之前已經有的是消不掉的,
於是有 if(!f[l][j-1]) continue;
這樣就可以保證之前的都已經滿足了,所以只要考慮當前段就好了
sum[i]表示到i的前綴和。
先獲取當前段大小now=sum[i] - sum[l]
所以如果now的第k位為0,並且之前的高位都不與當前ans沖突,就可以轉移了
因為如果高位與ans沖突,而前面已經最優了,所以如果沖突只能是不那麽優,
於是後面再優都補不回來了。其實這也是從高位貪心的意義所在
最後如果在f[n][A ~ B]中有任意一個為true的話,ans就不用修改(因為默認已經是0了)
而如果一個都沒有,那麽就將ans的當前位變成1,
那麽考慮如何判斷,
首先運算符肯定只能用or,不然是會遺漏信息的(也許可以用其他的吧,但是我沒想到)
考慮這樣兩個二進制串:
now :101100 1011
ans : 101100 0000
中間的空格代表把第k位和後面隔開,
通過一些二進制的基本性質(其實也是所有進制的基本性質)可以發現,
如果前面都是相同的話,因為ans的小於k的位數都還不確定,都是0,
所以最大相差也就是2^(k-1),
反之如果前面有沖突,那麽由於沖突在前面,相差一定會超過2^(k-1),
所以直接判斷now和ans的差就可以了?
不是!
因為now是中間過程,所以now是可能小於ans的,
所以如果是這樣的情況的話:
now : 100110 1011
ans : 101100 0000
這樣的話now < ans,所以now - ans 一定小於 2^(k-1),-1是因為第k位對應的是2^(k-1),幾次方是從0開始數的
但是這樣是不合法的,之所以這樣會被誤判為合法,是因為它之前的那個粗體0幫後面的粗體1抵消掉了。
因此為了這樣的0不會對真正不合法的1造成幹擾,
我們只需要計算(now | ans) - ans < 2^(k-1)是否成立即可,
因為這樣的話,所有這樣的0都會變為1,於是這時要再有不合法的1出現就無法被抵消了。
於是小數據就解決了,但是考慮到最大的點n <= 2000,這樣的復雜度肯定是過不去的
但是觀察發現,對於最大的那幾個點,滿足A == 1,
而之前的f數組之所以要開二維,還要枚舉段數,就是因為只有枚舉了所有情況才能保證不漏掉A~B之間的答案,
因為段數小不一定更優,不然可能會找到小於A的答案,
但是因為A == 1,就不可能會找到小於A的答案了,這時也滿足段數小的一定更優,
所以我們直接令g[i]表示dp到i,使第k位為0的最小段數,
然後轉移時判斷條件和上面一樣即可,註意初始化g[0]=0,其余為極大值
下面放代碼:
#include<bits/stdc++.h> using namespace std; #define R register int #define AC 2100 #define getchar() *o++ #define LL long long char READ[5000100],*o=READ; int n,A,B,all; int s[AC],g[AC]; LL sum[AC];//g[i]是適用於沒有下限的情況 LL ans; bool f[AC][250]; inline int read() { int x=0;char c=getchar(); while(c > ‘9‘ || c < ‘0‘) c=getchar(); while(c >= ‘0‘ && c <= ‘9‘) x=x*10+c-‘0‘,c=getchar(); return x; } void work1()//f[i][j]代表前i位分成j段,第k位能否在保證前k位都不變且不考慮後面all-k位的情況下是否可能與當前ans相同(此時第k位默認為0) {//這樣的話相當於給了一個判斷依據ans,於是就可以提高復雜度以解決問題 LL now;//獲取最高位數 bool done=false; all=log2(sum[n]) + 1; for(R k=all; k ;k--)//枚舉位數(從高位開始貪心) { memset(f,0,sizeof(f)); f[0][0]=true; done=false;//error!!!每次都要重置 for(R i=1;i<=n;i++)//枚舉到了哪裏 { int be=min(i,B); for(R j=1;j<=be;j++)//枚舉分的段數 { for(R l=j-1;l<i;l++)//枚舉上一段的結尾,error!!!上一段啊怎麽可以等於。。。。 { if(!f[l][j-1]) continue;//因為是|,所以前面有1不能消掉,所以前面不滿足就不能取了 now=sum[i] - sum[l];//獲取區間和 if(((now | ans) - ans) < (1LL<<(k-1)))//error!!!註意longlong {//如果相差小於當前位,代表不一樣的都在後面,就沒關系了 f[i][j]=true; break; } } if(i == n) { if(j >= A && j <= B && f[i][j]) { done=true; break;//如果找到了就無需再找了 } } } } if(!done) ans+=1LL<<(k-1);//因為1已經有一位了,所以只要移動k-1位就可以了(其實是因為本來就只要判斷2^(k-1),因為第一位對應的是2^0) } printf("%lld\n",ans); } //g[i]代表滿足這位為0最少需要的段數 void work2() { LL now; all=log2(sum[n]) + 1; for(R k=all; k ;k--) { memset(g,127,sizeof(g)); g[0]=0; for(R i=1;i<=n;i++)//枚舉樹 { for(R j=0;j<i;j++)//枚舉上一段的結尾,,,懶得特判1了,就從0開始吧 { now=sum[i] - sum[j]; if(((now | ans) - ans) < (1LL<<(k-1))) g[i]=min(g[i],g[j]+1);//error!!!註意longlong }//因為要求最小的,所以就不能跳過了 } /* printf("!!!%d:\n",k); for(int i=1;i<=n;i++) printf("%d ",g[i]); printf("\n");*/ if(g[n] > B) ans+=1LL<<(k-1);//error!!!貌似在這裏也要加LL吧 } printf("%lld\n",ans); } void pre() { n=read(),A=read(),B=read(); for(R i=1;i<=n;i++) s[i]=read(),sum[i]=sum[i-1] + (LL)s[i]; } int main() { freopen("in.in","r",stdin); // freopen("sculpture.in","r",stdin); // freopen("sculpture.out","w",stdout); fread(READ,1,5000000,stdin); pre(); if(A == 1) work2();//特殊點特殊處理 else work1(); fclose(stdin); // fclose(stdout); return 0; }
題解——[APIO2015]巴厘島的雕塑 貪心+DP+特殊數據優化