2019牛客暑期多校训练营(第八场)

传送门

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$;

  为什么是+i?

  因为在 [1,i] 比 [1,i-1] 多了个不同的数

  同理 [2,i],[3,i]....[i,i]一共多了i个

  ②[1,i-1] 中 ai 出现的最晚的位置为 j:

    $dp[i]=dp[i-1]+i-j$;

  为什么-j?

  比如x在 2,10,... j位置出现过

  那既然j位置出现过也就是在 [j,i-1] 区间出现过,

  必然在[j-1,i-1],[j-2,i-1] ...   [1,i-1]位置出现过

也就是多算了j次,要 -j

•代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 const int maxn=1e5+5;
 5 int pos[maxn];
 6 int a[maxn];
 7 ll dp[maxn];
 8 int main()
 9 {
10     int n;
11     cin>>n;
12     dp[1]=1;
13     for(int i=1;i<=n;i++)
14     {
15         cin>>a[i];
16         if(i>1)
17             dp[i]=dp[i-1]+i-pos[a[i]];
18         pos[a[i]]=i;
19     }
20     ll ans=0;
21     for(int i=1;i<=n;i++)
22         ans+=dp[i];
23     cout<<ans<<endl;
24 }
View Code

C.CDMA(构造)

•题意

  假设序列 s,t 都只含有 n 个元素;

  定义 $scdot t=sum_{i=1}^{i<=n}s_icdot 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( egin{array}{cc} 1 & 1 \ 1 & -1 end{array} ight)$

  m = 4:

  $left( egin{array}{cccc} 1 & 1 & 1 & 1 \ 1 & -1 & 1 & -1 \ 1 & 1 & -1 & -1 \ 1 & -1 & -1 & 1 \ end{array} ight)$

  m = 4 时,从行,列的中间分开,分成成 4 个 2×2 的子矩阵,你会发现,前三个子矩阵 = (m=2时的矩阵);

  最后一个子矩阵 = (m=2时的矩阵取反);

  根据上述规律,可以求出 m = 8,16,....,1024 时的满足条件的矩阵;

•代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 string base[2000];
 4 int m;
 5 void Slove()
 6 {
 7     for(int k=2;k<=10;k++)
 8     {
 9         m=1<<k;
10         for(int i=0;i<m/2;i++)
11             base[i]+=base[i]; ///double
12         for(int i=m/2;i<m;i++)
13         {
14             base[i]=base[i%(m/2)];
15 
16             for(int j=m/2;j<m;j++)
17                  base[i][j]=(base[i][j]=='0'?'1':'0');
18         }
19     }
20 }
21 int main()
22 {
23     int m;
24     cin>>m;
25     base[0]="11";
26     base[1]="10";
27     //0代表-1 便于用字符表示
28 
29     Slove();
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?'
':' ');
33 }
View Code

G.Gemstones(模拟)

•题意

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

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

  求最多可以消去多少次;

•思路

用栈模拟

当栈中大于两个时,往里加入时判断是否构成相同三字符,

若能构成则消去,不能构成则继续加入

代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 const int maxn=1e5+5;
 5 stack<char> sta;
 6 char s[maxn];
 7 int main()
 8 {
 9     scanf("%s",s+1);
10     int len=strlen(s+1);
11     bool flag;
12     int ans=0;
13 
14     for(int i=1;i<=len;i++)
15     {
16         flag=true;
17         while(flag)
18         {
19             flag=false;
20             if(sta.size()>=2)
21             {
22                 char a=sta.top();
23                 sta.pop();
24                 char b=sta.top();
25                 sta.pop();
26                 if(s[i]==a&&a==b)
27                 {
28                     flag=true;
29                     i++;
30                     ans++;
31                 }
32                 else
33                 {
34                     sta.push(b);
35                     sta.push(a);
36 
37                 }
38             }
39         }
40         sta.push(s[i]);
41     }
42 
43     printf("%d
",ans);
44 }
View Code
原文地址:https://www.cnblogs.com/MMMinoz/p/11332708.html