2019牛客暑期多校训练营(第八场)
A.All-one Matrices(单调栈)
•题意
给你一个只包含 0,1 的 n×m 的矩阵 s;
求只由 1 组成的矩阵的个数,并且这些矩阵不存在包含关系;
•题解
定义 h[ i ][ j ] 表示 ( i , j ) 位置及其之上的连续的 1 的个数;
那么,通过单调栈可以求出 ( i , j ) 位置的 l = L[ i ][ j ] 和 r = R[ i ][ j ];
当前这个只包含 1 的矩阵是否包含于其他更大的矩阵呢?
即如何判断当前这个矩阵对答案的贡献呢?
只需要判断 i+1 行的 [ l , r ] 列是否含有 r-l+1 个 1 即可;
如果 i+1 行的相应列含有 r-l+1 个 1,那么,由下一行的相同列组成的 1 矩阵势必要包含当前的矩阵;
如果当前矩阵对答案有贡献,是不是就让 ans++ 呢?
答案是否定的;
因为如果 ( i , j ) 位置之前的位置 ( i , x )(x < j) 的高度 h[ i ][ x ] 与 h[ i ][ j ] 相等的;
并且和 h[ i ][ j ] 所求的 1 矩阵相同,那么,当前这个矩阵就不能对答案有贡献;
这个通过单调栈求出的信息判断一下就好了;
•Code
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define INF 0x3f3f3f3f 4 #define INFll 0x3f3f3f3f3f3f3f3f 5 #define ll long long 6 #define pii pair<int ,int > 7 #define psi pair<string ,int > 8 #define pb(x) push_back(x) 9 #define ls(x) (x<<1) 10 #define rs(x) (x<<1|1) 11 #define GCD(a,b) __gcd(a,b) 12 #define PI acos(-1) 13 #define mem(a,b) memset(a,b,sizeof(a)) 14 #define endl '\n' 15 #define isLeap(x) (x%4==0&&x%100!=0||x%400==0) 16 #define Close() std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); 17 const int maxn=3e3+50; 18 19 int n,m; 20 char s[maxn][maxn]; 21 int h[maxn]; 22 int l[maxn]; 23 int r[maxn]; 24 int one[maxn]; 25 stack<int >sta; 26 vector<int >v[maxn]; 27 28 void Clear() 29 { 30 while(!sta.empty()) 31 sta.pop(); 32 } 33 void Work() 34 { 35 Clear(); 36 for(int i=1;i <= m;++i) 37 { 38 while(!sta.empty() && h[sta.top()] >= h[i]) 39 sta.pop(); 40 41 l[i]=sta.empty() ? 1:sta.top()+1; 42 sta.push(i); 43 } 44 Clear(); 45 for(int i=m;i >= 1;--i) 46 { 47 while(!sta.empty() && h[sta.top()] >= h[i]) 48 sta.pop(); 49 50 r[i]=sta.empty() ? m:sta.top()-1; 51 sta.push(i); 52 } 53 } 54 ll Solve() 55 { 56 mem(h,0); 57 58 ll ans=0; 59 for(int i=1;i <= n;++i) 60 { 61 for(int j=1;j <= m;++j) 62 { 63 if(s[i][j] == '1') 64 h[j]++; 65 else 66 h[j]=0; 67 } 68 69 Work();///单调栈 70 71 mem(one,0); 72 if(i != n) 73 { 74 for(int j=1;j <= m;++j) 75 one[j]=one[j-1]+(s[i+1][j] == '1'); 76 } 77 for(int j=0;j <= n;++j) 78 v[j].clear(); 79 80 for(int j=1;j <= m;++j) 81 { 82 int siz=v[h[j]].size(); 83 int cur=h[j]; 84 ///如果其前一个高度相同的位置与当前位置j的l[j],r[j]相同,那么当前位置无需计算 85 if(cur == 0 || siz != 0 && l[v[cur][siz-1]] == l[j] && r[v[cur][siz-1]] == r[j]) 86 continue; 87 88 int x=l[j]; 89 int y=r[j]; 90 v[h[j]].push_back(j); 91 92 if(one[y]-one[x-1] != y-x+1) 93 ans++;///如果i+1行的[x,y]列无y-x+1个1,那么ans++ 94 } 95 } 96 return ans; 97 } 98 int main() 99 { 100 // freopen("C:\\Users\\hyacinthLJP\\Desktop\\in&&out\\contest","r",stdin); 101 // freopen("C:\\Users\\hyacinthLJP\\Desktop\\in&&out\\contest","w",stdout); 102 scanf("%d%d",&n,&m); 103 for(int i=1;i <= n;++i) 104 scanf("%s",s[i]+1); 105 106 printf("%lld\n",Solve()); 107 108 return 0; 109 }
B.Beauty Values(记录结果再利用的DP)
•题意
给你一个序列 a,求序列 a 的任意一个区间 [l,r] 中,元素不同的个数的加和;
•题解
定义 dp[ i ] 表示以 i 为结尾的所有区间所包含的元素不同的数的个数;
即 $dp[i]=\sum_{j=1}^{j <= i}f\{j,i\}$,$f\{j,i\}$指的是[ j , i ]区间不同数的个数;
那么,对于 i 位置的数 ai:
①i 位置为 ai 首次出现的位置:
$dp[i]=dp[i-1]+i$;
②[1,i-1] 中 ai 出现的最晚的位置为 j:
$dp[i]=dp[i-1]+i-j$;
•Code
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define INF 0x3f3f3f3f 4 #define INFll 0x3f3f3f3f3f3f3f3f 5 #define ll long long 6 #define pii pair<int ,int > 7 #define psi pair<string ,int > 8 #define pb(x) push_back(x) 9 #define ls(x) (x<<1) 10 #define rs(x) (x<<1|1) 11 #define GCD(a,b) __gcd(a,b) 12 #define PI acos(-1) 13 #define mem(a,b) memset(a,b,sizeof(a)) 14 #define endl '\n' 15 #define isLeap(x) (x%4==0&&x%100!=0||x%400==0) 16 #define Close() std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); 17 const int maxn=1e5+50; 18 19 int n; 20 int a[maxn]; 21 int p[maxn]; 22 ll dp[maxn]; 23 24 int main() 25 { 26 // freopen("C:\\Users\\hyacinthLJP\\Desktop\\in&&out\\contest","r",stdin); 27 // freopen("C:\\Users\\hyacinthLJP\\Desktop\\in&&out\\contest","w",stdout); 28 scanf("%d",&n); 29 30 mem(dp,0); 31 mem(p,0); 32 33 for(int i=1;i <= n;++i) 34 scanf("%d",a+i); 35 36 for(int i=1;i <= n;++i) 37 { 38 dp[i]=dp[i-1]+i; 39 40 dp[i] -= p[a[i]]; 41 p[a[i]]=i; 42 } 43 44 // for(int i=1;i <= n;++i) 45 // printf("i=%d,dp=%lld\n",i,dp[i]); 46 ll ans=0; 47 for(int i=1;i <= n;++i) 48 ans += dp[i]; 49 50 printf("%lld\n",ans); 51 52 return 0; 53 }
C.CDMA(构造)
•题意
假设序列 s,t 都只含有 n 个元素;
定义 $s\cdot t=\sum_{i=1}^{i<=n}s_i\cdot t_i$ ;
构造一个 m×m 的矩阵 a,其中 m = 2k , 1 ≤k ≤ 10,使其满足:
①矩阵中只包含 -1,1;
②任意不同的两行 a[ i ],a[ j ], a[ i ]·a[ j ] = 0;
•题解
根据 m = 2 , m = 4 的满足条件的矩阵找规律;
m = 2:
$\left( \begin{array}{cc} 1 & 1 \\ 1 & -1 \end{array}\right)$
m = 4:
$\left( \begin{array}{cccc} 1 & 1 & 1 & 1 \\ 1 & -1 & 1 & -1 \\ 1 & 1 & -1 & -1 \\ 1 & -1 & -1 & 1 \\ \end{array}\right)$
看出啥了没?
m = 4 时,从行,列的中间劈开,劈成 4 个 2×2 的子矩阵,你会发现,前三个子矩阵 = (m=2时的矩阵);
最后一个子矩阵 = (m=2时的矩阵取反);
根据上述规律,可以求出 m = 8,16,....,1024 时的满足条件的矩阵;
•Code
View Code1 #include<bits/stdc++.h> 2 #define mem(a,b) memset(a,b,sizeof(a)) 3 using namespace std; 4 const int maxn=(1<<10)+50; 5 6 int m; 7 string base[maxn]; 8 9 int main() 10 { 11 base[0]="11"; 12 base[1]="10";///0表示-1 13 14 for(int k=2;k <= 10;++k) 15 { 16 m=1<<k; 17 for(int i=0;i < m/2;++i) 18 base[i] += base[i]; 19 20 for(int i=m/2;i < m;++i) 21 { 22 base[i]=base[i%(m/2)]; 23 for(int j=m/2;j < m;++j) 24 base[i][j]=(base[i][j] == '0' ? '1':'0'); 25 } 26 } 27 28 int m; 29 scanf("%d",&m); 30 for(int i=0;i < m;++i) 31 for(int j=0;j < m;++j) 32 printf("%d%c",base[i][j] == '0' ? -1:1,j == m-1 ? '\n':' '); 33 return 0; 34 }
G.Gemstones(模拟)
•题意
给你一个只包含大写字母的串 s;
如果相邻的三个字符为相同的字母,那么,便可将这三个相同的字母消去;
求最多可以消去多少次;
•题解
用 vector 模拟;
每次加入一个新字符,判断他和 vector 中的后两个字符是否为相同字符,如果是,消去即可;
•Code
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define INF 0x3f3f3f3f 4 #define INFll 0x3f3f3f3f3f3f3f3f 5 #define ll long long 6 #define pii pair<int ,int > 7 #define psi pair<string ,int > 8 #define pb(x) push_back(x) 9 #define ls(x) (x<<1) 10 #define rs(x) (x<<1|1) 11 #define GCD(a,b) __gcd(a,b) 12 #define PI acos(-1) 13 #define mem(a,b) memset(a,b,sizeof(a)) 14 #define endl '\n' 15 #define isLeap(x) (x%4==0&&x%100!=0||x%400==0) 16 #define Close() std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); 17 const int maxn=1e5+50; 18 19 char s[maxn]; 20 vector<char >p; 21 22 int main() 23 { 24 // freopen("C:\\Users\\hyacinthLJP\\Desktop\\in&&out\\contest","r",stdin); 25 // freopen("C:\\Users\\hyacinthLJP\\Desktop\\in&&out\\contest","w",stdout); 26 scanf("%s",s+1); 27 28 ll ans=0; 29 int len=strlen(s+1); 30 for(int i=1;i <= len;++i) 31 { 32 p.pb(s[i]); 33 34 int n=p.size(); 35 if(n >= 3 && p[n-1] == p[n-2] && p[n-2] == p[n-3]) 36 { 37 ans++; 38 for(int j=1;j <= 3;++j) 39 p.erase(p.end()-1); 40 41 } 42 } 43 printf("%lld\n",ans); 44 45 return 0; 46 }