Round 14:
Problem C:
題意:n*m(01)矩陣,n,m<=1e3.求最大包含全1的子矩形並且其被0包圍(外圍一圈必須為0).
畫幾個範例 可以發現任意一個合法的全1子矩形都形成一個全'1'聯通分量.
dfs搜出全1的聯通分量 並記錄左上和右下位置.不能落在邊界.
在利用行/列首碼判斷左上和右下一圈是否都為0,二維首碼和判斷搜出來的子矩形是否全部為1即可.
#include <bits/stdc++.h>using namespace std;typedef long long ll;const int N=2e3+20,inf=0x3f3f3f3f;const ll mod=1e9+7;int n,m,vis[N][N],r[N][N],c[N][N],a[N][N],f[N][N]; int lef,rig,up,dow;int dx[]={-1,1,0,0};int dy[]={0,0,-1,1};void dfs(int x,int y){vis[x][y]=1;rig=max(rig,y),lef=min(lef,y);up=min(up,x),dow=max(dow,x);for(int i=0;i<4;i++){int c=x+dx[i],d=y+dy[i];if(c>=1&&c<=n&&d>=1&&d<=m&& !vis[c][d] &&a[c][d]==1)dfs(c,d);}}bool check(){int x1=up,y1=lef,x2=dow,y2=rig;bool flag=true;//cout<<x1<<' '<<y1<<' '<<x2<<' '<<y2<<endl;int num=(rig-lef+1)*(dow-up+1);int act=f[x2][y2]-f[x2][y1-1]-f[x1-1][y2]+f[x1-1][y1-1];if(num!=act)flag=false;//cout<<num<<' '<<act<<endl;x1--,y1--,x2++,y2++;int r0=y2-y1+1,c0=x2-x1+1;int r1=r[x1][y2]-r[x1][y1-1],r2=r[x2][y2]-r[x2][y1-1];int c1=c[y1][x2]-c[y1][x1-1],c2=c[y2][x2]-c[y2][x1-1];//cout<<r0<<' '<<c0<<endl;//cout<<r1<<' '<<r2<<endl;//cout<<c1<<' '<<c2<<endl;if(r1!=r0||r2!=r0)flag=false;if(c1!=c0||c2!=c0)flag=false;if(x1<=0||x2>n||y1<=0||y2>m)flag=false;return flag;}int main(){ cin>>n>>m;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&a[i][j]),r[i][j]=r[i][j-1]+(a[i][j]==0);for(int j=1;j<=m;j++)for(int i=1;i<=n;i++)c[j][i]=c[j][i-1]+(a[i][j]==0);for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)f[i][j]=a[i][j]+f[i][j-1]+f[i-1][j]-f[i-1][j-1];int ans=-1;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){if(a[i][j]==1&& !vis[i][j]){lef=up=inf;rig=dow=0;dfs(i,j);if(check())ans=max(ans,(rig-lef+1)*(dow-up+1));}}cout<<ans<<endl;return 0;} Problem D
題意:給出長度為n的序列c,求所有區間長度為[a,b]之間的異或和之和,n<=1e5,c[i]<=1e9.
異或和的累加和 -> 一種套路,就是將按二進位每位的貢獻來算累加和.
也就是說,算有多少個區間其異或和的第b位為1,則第b位總貢獻為num * (1<<b).
固定當前位b以後,處理出首碼第b位異或的值.
現在枚舉右端點j,則左端點在[j-B+1,j-A+1]間.
若首碼j第b位異或為0 則需要知道首碼[j-B,j-A]之間第b位異或為1的個數,用cnt[0/1]來記錄這段區間即可.
#include <bits/stdc++.h>using namespace std;typedef long long ll;const int N=2e5+20;const ll mod=1e9+7; ll n,c[N],a,b;ll pre[N];int main(){ cin>>n>>a>>b;for(int i=1;i<=n;i++)scanf("%lld",&c[i]);ll ans=0;pre[0]=0;for(int bit=0;bit<=30;bit++){int cnt[2]={0,0};for(int i=1;i<=n;i++){pre[i]=pre[i-1]^((c[i]>>bit)&1);if(i-b-1>=0)cnt[pre[i-b-1]]--;//del old timeif(i-a>=0)cnt[pre[i-a]]++;//add newans=(ans+cnt[pre[i]^1]*(1ll<<bit))%mod;}}cout<<ans<<endl;return 0;} Problem E:
題意:有多少個n個結點的orederd樹,其直徑<=m? n,m<=300.
ordered tree:結點從根開始從左至右編號,若兩棵樹存在邊,其端點編號不同,則這兩個樹就算是不同的.
直徑要<=m 則根結點串連的兩個子樹其高度相加在加1要<=m.
令dp[i][j]為 i個結點,高度為j的order tree個數,(轉移時要注意其直徑要<=m).
因為tree是ordered.所以按照根第一次串連的子樹其高度來轉移狀態.
dp[i][j]
根只有一個子樹,dp[i][j]+=dp[i-1][j-1].
根有若干個子樹:
第一個子樹的高度為j-1,枚舉其大小為k,則dp[i][j]+=dp[k][j-1]*dp[i-k][x] (x=1...min(j,m-j)).
第一個子樹高度為j',大小為k,則剩餘部分高度要為j,dp[i][j]+=dp[k][j']*dp[i-k][j] (j'=1..min(j-2,m-j-1))
直接計算時間複雜度為O(n^4)
因為dp時j'和x都是從某個值累加到某個值,想到用首碼和sum[i][j]=dp[i][1..j]來最佳化一下就能得到O(n^3).
#include <bits/stdc++.h>using namespace std;typedef long long ll;const int N=3e2+20;int n,m;ll mod,dp[N][N],sum[N][N];//sum[i][j]= dp[i][1]+dp[i][2]+..dp[i][j]. int main(){ cin>>n>>m>>mod;dp[1][0]=1;for(int j=0;j<=m;j++)sum[1][j]=1;for(int i=2;i<=n;i++){for(int j=1;j<=m;j++){dp[i][j]=(dp[i][j]+dp[i-1][j-1])%mod;for(int k=1;k<=i-2;k++){//dp[i][j]+=dp[k][j-1]*dp[i-k][x],x=[1..min(j,m-j)].int x=min(j,m-j);dp[i][j]=(dp[i][j]+(dp[k][j-1]*sum[i-k][x])%mod)%mod;//dp[i][j]+=dp[k][x]*dp[i-k][j] , x=[1..min(j-2,m-j-1)]x=min(j-2,m-j-1);dp[i][j]=(dp[i][j]+(dp[i-k][j]*sum[k][x])%mod)%mod;}sum[i][j]=(sum[i][j-1]+dp[i][j])%mod;}} cout<<sum[n][m]<<endl;return 0;}