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

  1 #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 }
View Code

 


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

 1 #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 }
View Code

 


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

 1 #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 }
View Code

 


G.Gemstones(模拟)

•题意

  给你一个只包含大写字母的串 s;

  如果相邻的三个字符为相同的字母,那么,便可将这三个相同的字母消去;

  求最多可以消去多少次;

•题解

  用 vector 模拟;

  每次加入一个新字符,判断他和 vector 中的后两个字符是否为相同字符,如果是,消去即可;

•Code

 1 #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 }
View Code

 

posted @ 2019-08-10 19:21  HHHyacinth  阅读(358)  评论(0编辑  收藏  举报