關於PJ 10.27
題1 : Orchestra
題意:
給你一個 n*m 的矩陣,其中有一些點是被標記過的。
現在讓你求標記個數大於 k 個的二維區間個數。
n、m 、k 最大是 10 。
分析:
part 1:
10 的範圍 ,直接暴力 $O(n^{6})$ 也能過,
具體就是列舉區間的上邊界、下邊界、左邊界、右邊界,然後暴力累加這個區間內所有的標記點個數。
程式碼不給出(因為比 part 2 的程式碼還長)。
part 2:
(考慮的是演算法的優化,可以選擇性看)
我們可以考慮用矩陣字首和優化。
字首和就是記錄從開頭到某個位置上所有值的和,這個大家應該都知道。
那麼二維其實就是記錄從開頭(一般是 ($1,1$))到某個位置(比如 ($i,j$))上所有值的和。
這個可以看程式碼領會(其中 $val[i][j]$ 表示當前點是否被標記):
1for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) 2mp[i][j]=val[i][j]+mp[i-1][j]+mp[i][j-1]-mp[i-1][j-1];
這裡 我們假設 $ mp[i-1,j],mp[i][j-1],mp[i-1][j-1] $ 已經處理好了,那麼 $mp[i-1][j], mp[i][j-1], mp[i-1][j-1]$ 與 mp[i][j] 的關係如下:
那麼我們要求某個矩形區間的標記點個數的辦法也是差不多的。
比如要求出 u,l 到 d,r 這段區間 的標記點數量,那麼答案就是: mp[d][r]+mp[u-1][l-1]-mp[u-1][r]-mp[d][l-1].
程式碼如下:
1 //by Judge 2 #include<cstdio> 3 #include<iostream> 4 using namespace std; 5 const int M=33; 6 #ifndef Judge 7 #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 8 #endif 9 char buf[1<<21],*p1=buf,*p2=buf; 10 inline int read(){ int x=0,f=1; char c=getchar(); 11for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; 12for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; 13 } int n,m,c,k,ans,mp[M][M]; 14 inline int check(int l,int r,int u,int d){ 15return mp[d][r]+mp[u-1][l-1]-mp[u-1][r]-mp[d][l-1]; 16 } 17 int main(){ 18n=read(),m=read(), 19c=read(),k=read(); 20for(int i=1,x,y;i<=c;++i) 21x=read(),y=read(),++mp[x][y]; 22for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) 23mp[i][j]+=mp[i-1][j]+mp[i][j-1]-mp[i-1][j-1]; 24 25for(int u=1;u<=n;++u) for(int d=u;d<=n;++d) 26for(int l=1;l<=m;++l) for(int r=l;r<=m;++r) 27if(check(l,r,u,d)>=k) ++ans; 28 29return printf("%d\n",ans),0; 30 }
part 3:
(分析過度,不宜觀看)
這道題的資料有點小啊?如果說是100 的範圍呢?(1000的話可能過大了,不過應該也不是沒辦法解)
我們可以觀察這區間之間的單調性:
假設我們固定了邊界:r、u、d 作為右邊界、上邊界和下邊界,那麼左邊界越靠左,整個區間所會包含的標記點是單調不減的。
然後這裡就要用二分,如果你想知道具體內容的話就 Q 我吧(不要羞澀,撩就行了),我懶得寫了。
程式碼也不給出了(作者懶癌晚期)
評價:
大水題,應該拿滿分。
題2 : 質數
題意:
讓你求一個區間範圍內滿足條件的數的個數,詢問有多組。
其中滿足條件的數為:1.質數; 2.兩個質數的乘積
分析:
只要你學過篩質數的話,這道題應該十分鐘過的,又沒什麼套路。
篩質數的話建議用尤拉篩法(關於尤拉,他非常牛皮,很多數論演算法都是他發明的,可自行了解)
題目沒什麼亮點,就是篩完質數後預處理一下字首資訊,詢問的時候O(1) 回答就好了
程式碼如下:
1 //by Judge 2 #include<cstdio> 3 #include<iostream> 4 #define ll long long 5 using namespace std; 6 const int M=1e7+11; 7 const int MX=1e7; 8 #ifndef Judge 9 #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 10 #endif 11 char buf[1<<21],*p1=buf,*p2=buf; 12 inline int read(){ int x=0,f=1; char c=getchar(); 13for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; 14for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; 15 } char sr[1<<21],z[20];int C=-1,Z; 16 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;} 17 inline void print(int x,char chr='\n'){ 18if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x; 19while(z[++Z]=x%10+48,x/=10); 20while(sr[++C]=z[Z],--Z);sr[++C]=chr; 21 } int T,cnt,f[M],is[M],in[M],prim[M]; 22 inline void prep(){ is[1]=1; 23for(int i=2;i<=MX;++i){ 24if(!is[i]) prim[++cnt]=i,in[i]|=1; 25for(int j=1;j<=cnt&&i*prim[j]<=MX;++j){ 26is[i*prim[j]]|=1; 27if(!is[i]) in[i*prim[j]]|=1; 28if(i%prim[j]==0) break; 29} 30} 31 } 32 int main(){ 33T=read(),prep(); 34for(int i=1;i<=MX;++i) 35f[i]=f[i-1]+in[i]; 36for(int l,r;T;--T) 37l=read(),r=read(), 38print(f[r]-f[l-1]); 39return Ot(),0; 40 }
評價:
沒什麼難度的,最好 A 掉,起步 90 分(話說這題不卡常,基本只有 0 和 100 )
題3 :Hanoi Factorys
題意:
幾個空心環,讓你依次疊高,要求下面的外徑比上面的大,且內徑下小上大,求最大高度。
分析:
這題有點鬼畜,還好之前打過直接 A 了。
但其實沒什麼難的,首先維護外徑單調遞增,外徑相同比較內徑,內徑大的放下面,
為什麼外徑相同則內徑大的放下面?
因為這樣能保證上面的環內徑小,那麼就能容納外徑更小的環。
然後單調棧求解。
想學單調棧的看看這篇部落格(順便可以把單調佇列學了): 3609/article/details/78806089" target="_blank" rel="nofollow,noindex">click here
然後刷一下這些題目就差不多會了: click here
//by Judge 1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<vector> 5 #include<cstdio> 6 #include<cmath> 7 #include<queue> 8 #define P make_pair 9 #define ll long long 10 using namespace std; 11 const int M=1e6+100; 12 const int inf=1e9; 13 inline int read(){ 14int x=0,f=1; char c=getchar(); 15for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; 16for(;isdigit(c);c=getchar()) x=x*10+c-'0'; 17return x*f; 18 } 19 int n,head=1,tail; 20 priority_queue< pair<ll,ll> > q; 21 ll res=-inf; 22 struct Node{ 23ll a,b,h; 24friend bool operator < (const Node& x,const Node& y){ 25return x.b!=y.b ? x.b>y.b : x.a>y.a; //b相同則先放a大的漢諾塊 26} 27 }p[M]; 28 29 int main(){ 30n=read(); 31for(int i=0;i<n;++i) 32p[i].a=read(), 33p[i].b=read(), 34p[i].h=read(); 35sort(p , p+n); 36 37for(int i=0;i<n;++i){ 38ll tmp=p[i].h; 39while(!q.empty() && q.top().second>=p[i].b) 40q.pop(); 41if(!q.empty()) tmp+=q.top().first; 42q.push(P(tmp,p[i].a)); 43res=max(res , tmp); 44} 45cout<<res<<endl; 46return 0; 47 }
評價:
沒 A 掉?沒關係,抓緊在剩下的時間提升自己,為戰鬥做準備,拿個 50+ 的暴力分差不多,最好當然是 A 掉。
結論:
OI 選手還是多學點演算法的好。
題4 :Distinct Paths
題意:
題意有點繞,就是讓你求出一個矩陣中滿足條件的方案數。
條件是:對於從 1,1 到 n,m 的任意一條路徑,使得路徑上的點的顏色互不相同。
難點在於:有些點的顏色已經給出。
如果所有的點的顏色都不固定,這就是道數學題。
分析:
有點複雜,不會也正常(畢竟說實話我也沒有解出來_(:з」∠)_)
建議學會搜尋以及簡單的狀壓(不是狀壓dp,但是 狀壓 dp 這個東西學一下也行)
首先你要發現題目的資料範圍是假的。
因為 k 很小,最大隻有 10 ,只要 n+m-1 大於 k 了,那麼必然不存在任意一種滿足條件的方案。
這點還是不難發現的。
然後你不能太優秀。
也就是不能一開始想的是 狀壓dp ,否則你就會陷入無休無止的修改狀態表示以及思考狀態轉移之中。。。(好吧我就是這樣耗了一個多小時)
其次你要會搜尋。
這裡搜尋+剪枝才是本題正確開啟方式。
另外 noip 普及一般都有搜尋題,而且爆搜適用於任何題目的騙分,如果加了剪枝或者記憶化之類的那就基本是正解
最後你要會剪枝。
這道題的亮點,各種鬼畜剪枝。
搜尋題,基本考的是剪枝,就算不考剪枝,大多也是可以用剪枝卡時間的。
(卡時間真的非常重要,你怎麼知道一個小小的剪枝會不會神奇讓你分數過線?)
程式碼如下:
1 //by Judge 2 #include<cstdio> 3 #include<iostream> 4 using namespace std; 5 const int M=(1<<10)|3; 6 const int mod=1e9+7; 7 #ifndef Judge 8 #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 9 #endif 10 char buf[1<<21],*p1=buf,*p2=buf; 11 inline int read(){ int x=0,f=1; char c=getchar(); 12for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; 13for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; 14 } int n,m,K,mp[13][13],cant[13][13],vis[13]; 15 inline void MOD(int& a){ if(a>mod) a-=mod; } 16 #define lowbit(x) (x&-x) 17 int dfs(int x,int y){ 18if(y>m) y=1,++x; 19if(x>n) return 1; 20cant[x][y]=cant[x-1][y]|cant[x][y-1]; 21int dx=x,dy=y+1,num=0; 22int S=cant[x][y],T=cant[x][y]; 23while(S) S-=lowbit(S),++num; 24if(K-num<n+m-x-y+1) return 0; 25 26int ans=0,tmp=-1; 27for(int i=1;i<=K;++i){ 28if((T>>i-1)&1) continue; 29if(!mp[x][y]||mp[x][y]==i){ 30cant[x][y]=T|(1<<i-1),++vis[i]; 31if(vis[i]==1) ans+=(tmp>=0?tmp:tmp=dfs(dx,dy)); 32else ans+=dfs(dx,dy); MOD(ans),--vis[i]; 33} 34} return ans; 35 } 36 int main(){ 37n=read(),m=read(),K=read(); 38if(n+m-1>K) return puts("0"),0; 39for(int i=1;i<=n;++i) 40for(int j=1;j<=m;++j){ 41mp[i][j]=read(); 42++vis[mp[i][j]]; 43} 44printf("%d\n",dfs(1,1)); 45return 0; 46 }
作為搜尋 A 掉了去年普及第三題的 OI 選手,沒有A掉這題,我只能說,老了。
評價:
略(guo)顯(fen)毒瘤, 拿個暴力分(這個好像沒有)。。。 額,能拿多少是多少,越多越好