程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> BZOJ 2241 SDOI2011 打地鼠 線性篩+二階差分

BZOJ 2241 SDOI2011 打地鼠 線性篩+二階差分

編輯:C++入門知識

BZOJ 2241 SDOI2011 打地鼠 線性篩+二階差分


首先聲明:此題不滿足二分條件,一切寫二分的題解均為誤解 請注意辨明!

題目大意:給定一個m*n的洞穴矩陣,每個洞穴裡面有若干地鼠,我們需要選定一個r*c的錘子進行擊打,每次擊打必須保證r*c的范圍內所有洞穴均有地鼠,且每次擊打只會打掉每個洞穴恰好一只地鼠,求最小擊打次數

m,n<=100

考慮一個1*8的洞穴,當我們把錘子設作1*4時可以完成擊打,而1*3不能 故不滿足單調性,二分不正確

但是一個性質是確定的:假設我們選定一個3*4的錘子可以完成擊打,那麼我們選定一個3*2的錘子也一定能完成擊打

那麼反過來,當我們選擇一個3*2的錘子無法完成擊打時,那麼3*4的錘子一定無法完成擊打

說白了這題是一個偏是積性函數

於是這題我們選擇線性篩進行篩選 若r相同時c1無法完成擊打,那麼c1*p也無法完成擊打

驗證的話 首先選定錘子時打的方案是一定的,於是我們貪心,從左上至右下依次擊打,每個位置擊打的次數等於擊打區域左上角洞穴的當前地鼠數量,當擊打後任意洞穴地鼠數量不為零

然後驗證的時候強行模擬是O( (mn)^2 )樹套樹可以變成O( mn*logm*logn )

但是這道題有修改而沒有查詢(每次查詢節點時前面必須為零) 故我們選擇O(1)修改O(n^2)查詢的二階差分

向左向上進行兩次差分 然後每次修改如圖

\

擊打後整個區域都為零則可以完成擊打

此外這題O(n^4)暴力都能過 而且只比正解慢了四毫秒 就連O(n^6)的暴力加點剪枝都過了0.0 什麼情況究竟

<喎?http://www.Bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHByZSBjbGFzcz0="brush:java;">#include #include #include #include #define M 110 using namespace std; int m,n,cnt,a[M][M],map[M][M],sum,ans=0x7f7f7f7f; const int prime[]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97},tot=25; int not_able[2][M]; bool Judge(int x,int y) { int i,j; if( sum%(x*y) ) return 0; memcpy(map,a,sizeof a); for(i=1;i<=m;i++) { for(j=1;j<=n;j++) { if(map[i][j]<0) return 0; if(i<=m-x+1&&j<=n-y+1) { int num=map[i][j]; map[i][j]-=num; map[i+x][j]+=num; map[i][j+y]+=num; map[i+x][j+y]-=num; } if(map[i][j]>0) return 0; } } ans=min(ans,sum/x/y); return 1; } bool Linear_Shaker(int num,bool flag,int Time) { int i,j; bool re=0; for(i=1;i<=(flag?n:m);i++) { if(not_able[flag][i]!=Time) { if(flag) not_able[flag][i]=(Judge(num,i)?0:Time); else not_able[flag][i]=(Linear_Shaker(i,1,++cnt)?0:Time); } if(not_able[flag][i]!=Time) re=1; if(i^1) for(j=1;j<=tot&&i*prime[j]<=(flag?n:m);j++) { if(not_able[flag][i]==Time||not_able[flag][prime[j]]==Time) not_able[flag][i*prime[j]]=Time; if(i%prime[j]==0) break; } } return re; } int main() { int i,j; cin>>m>>n; for(i=1;i<=m;i++) for(j=1;j<=n;j++) scanf("%d",&a[i][j]),sum+=a[i][j]; for(i=m;i;i--) for(j=n;j;j--) a[i][j]-=a[i-1][j]; for(i=m;i;i--) for(j=n;j;j--) a[i][j]-=a[i][j-1]; Linear_Shaker(0,0,++cnt); cout<

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved