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

传送门

B.Crazy Binary String(前缀和)

•题意

  给你一个只包含 0,1 的串 s;

  求满足 0 与 1 的个数相同的子串和子序列;

  输出这两个串的最大长度;

•思路

找01个数相同的子串,类似于这个(A题),(话说比那个简单有没有

根据前缀和求增量,如果增量相同的话,那这段区间里01个数就相同,然后每次取最大的ans

找01个数相同的子序列,那肯定是01中个数少的那个所组成的,输出两倍就好了

•代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 map<int,int> mp;
 4 char s[10005];
 5 int zero,one;
 6 int ans;
 7 int main()
 8 {
 9     int n;
10     cin>>n;
11     mp[0]=0;
12     for(int i=1;i<=n;i++)
13     {
14         cin>>s[i];
15         if(s[i]=='0')
16             zero++;
17         else
18             one++;
19         if(mp.count(zero-one))
20             ans=max(ans,i-mp[zero-one]);
21         else
22             mp[zero-one]=i;
23     }
24     cout<<ans<<' '<<min(zero,one)*2<<endl;
25 }
View Code

 

D.Big Integer(数论)

•题意

$A_{n}=underbrace{1......1}_{n}$

$A_{i^j}$ ≡ 0 (mod p)的个数,其中 1 ≤ i ≤ n , 1 ≤ j ≤ m;

•思路

写起来有点麻烦...具体戳这里

防止忘记推导过程,记一下重点...

•代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 int p,n,m;
 5 ll ans;
 6 int P[105];
 7 int x[105];
 8 
 9 ll qpow(ll a,ll b,ll mod)
10 {
11     ll res=1;
12     while(b)
13     {
14         if(b&1)
15             res=res*a%mod;
16         a=a*a%mod;
17         b>>=1;
18     }
19     return res;
20 }
21 
22 int F()//因子分解得到最小循环节d,Max(d)=p-1
23 {
24     int d=p-1;
25     for(int i=1;i*i<=p-1;i++)
26     {
27         if((p-1)%i==0)
28         {
29             if(qpow(10,i,p)==1)
30                 d=min(d,i);
31             if(qpow(10,(p-1)/i,p)==1)
32                 d=min(d,(p-1)/i);
33         }
34     }
35     return d;
36 }
37 
38 int PrimeF(int d)//质因子分解 得到pi,xi,k
39 {
40     int k=0;
41     for(int i=2;i*i<=d;i++)
42     {
43         if(d%i==0)
44         {
45             P[++k]=i;
46             x[k]=0;
47         }
48         while(d%i==0)
49         {
50             x[k]++;
51             d/=i;
52         }
53     }
54     if(d!=1)
55     {
56         P[++k]=d;
57         x[k]=1;
58     }
59     return k;
60 }
61 
62 void Slove()
63 {
64     int d=F();
65     int k=PrimeF(d);
66     int maxX=*max_element(x+1,x+1+k);
67     for(int j=1;j<=m;j++)//固定j,求个数
68     {
69         ll g=1;
70         for(int i=1;i<=k;i++)
71             g*=qpow(P[i],(x[i]+j-1)/j,p);  //p^(x+j-1)/j
72         ans+=n/g;
73 
74         if(j>=maxX)//此时g为最小不再变化,共有(n/g)*(m-j)种
75         {
76             ans+=n/g*(m-j);
77             break;
78         }
79     }
80 }
81 
82 int main()
83 {
84     int t;
85     cin>>t;
86     while(t--)
87     {
88         cin>>p>>n>>m;
89         ans=0;
90         if(p==3)
91             ans=n/3*m;
92         else if(p==2||p==5)
93             ans=0;
94         else
95             Slove();
96         cout<<ans<<endl;
97     }
98 }
View Code

 

F.Planting Trees(单调队列优化)

•题意

  在一块 n×n 的土地上植树;

  第(i,j) 块有棵高为 a[ i ][ j ] 树;

  必须要找一片满足任意两棵树的高度差都不超过 m 的矩形区域;

  并且想要这个矩形区域面积尽可能大;

  求出这个矩形区域,输出这个矩形区域包含的树的个数;

•思路

注意:

单调队列代码实现时

①要分清楚值和索引,蟹蟹可爱的猪猪帮我找bug,此致敬礼!

②在出队列时(head++时)要注意队列非空,借鉴猪猪的经验!

•代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=505;
 4 int a[maxn][maxn];
 5 int demin[maxn];
 6 int demax[maxn];
 7 int Min[maxn];
 8 int Max[maxn];
 9 int n,m;
10 int ans;
11 
12 int main()
13 {
14     int t;
15     cin>>t;
16     while(t--)
17     {
18         cin>>n>>m;
19         ans=0;
20         for(int i=1;i<=n;i++)
21             for(int j=1;j<=n;j++)
22                 cin>>a[i][j];
23 
24 
25         for(int i=1;i<=n;i++)
26         {
27             //初始化
28             for(int j=1;j<=n;j++)
29                 Min[j]=Max[j]=a[i][j];
30 
31             //枚举上下边界
32             //从第i行到第k行求最优解
33             for(int k=i;k<=n;k++)
34             {
35                 for(int j=1;j<=n;j++)
36                 {
37                     //维护当前上下边界第i到第k行 每列的最大值最小值
38                     Min[j]=min(Min[j],a[k][j]);
39                     Max[j]=max(Max[j],a[k][j]);
40                 }
41 
42                 int l=1,r=1;
43                 int headmin=1,headmax=1;
44                 int tailmin=0,tailmax=0;
45                 //枚举右边界r
46                 //维护两个单调队列
47                 while(r<=n)
48                 {
49                     //单调递增队列,头是最小值
50                     while(headmin<=tailmin&&Min[demin[tailmin]]>=Min[r])
51                         tailmin--;
52                     demin[++tailmin]=r;
53                     //单调递减队列,头是最大值
54                     while(headmax<=tailmax&&Max[demax[tailmax]]<=Max[r])
55                         tailmax--;
56                     demax[++tailmax]=r;
57                     
58                     //每个右边界确定后(也就是在此右边界下)
59                     //找符合条件并使面积尽量大的左边界
60                     while(l<=r&&Max[demax[headmax]]-Min[demin[headmin]]>m)
61                     {
62                         l++;
63                         while(headmax<=tailmax&&demax[headmax]<l)
64                             headmax++;
65                         while(headmin<=tailmin&&demin[headmin]<l)
66                             headmin++;
67                     }
68                     ans=max(ans,(r-l+1)*(k-i+1));
69                     r++;//枚举下一个右边界 找最大值
70                 }
71             }
72         }
73         cout<<ans<<endl;
74     }
75 }
View Code

G.Removing Stones(假博弈 思维?)

•题意

有n堆石头,如果石头总数是奇数的话,就从最小堆中拿走一块

如果总数是偶数堆或者是减少石头后,那每次从不为0的任意两堆中同时拿走一块

如果最后都拿完,则Mark胜利

现在给出每堆石头数,问存在多少对[l,r]使得可以有优胜情况

•思路

可以想一种极端情况,所有的堆都和最大的一堆一起拿

那最大的一堆个数就是除本身以外所有堆的和

通常情况就是其他堆之间也可以一起拿,

那这个问题就转化成了

有多少个区间使得在这个区间里,最大的堆<=其他堆的和

貌似不太好找...但是!有一句话叫做正难则反!

那我们可以用所有的情况-不符合的情况_(:з」∠)_

那怎么找不符合的情况呢?

我们可以通过枚举每一个堆 i,以这个堆为最大堆

利用 最大的堆>其他堆的和,找到极限左边界,

然后枚举左边界,通过固定的左边界L,找到右边界R

那这个左边界L所构成情况就是 L—i,L—i+1...... L—R

也就是每个L都有(R-i+1)种不符合的情况

•代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 #define N 300005
 5 ll T,n,m;
 6 ll a[N];
 7 
 8 int main()
 9 {
10     cin>>T;
11     while(T--)
12     {
13         cin>>n;
14         ll ans=(ll)n*(n+1)/2;
15         for(ll i=1;i<=n;i++)
16             cin>>a[i];
17         for(ll i=1;i<=n;i++)//枚举堆i
18         {
19             ll l=i,r=i,sum=0;
20             while(l>1&&a[i]>sum+a[l-1])   //对于每一个a[i],找符合条件的最左边界
21                 l--,sum+=a[l];
22 
23             for(int L=l;L<=i;L++)         //枚举L
24             {
25                 while(r<n&&a[i]>sum+a[r+1])//固定L,找到最右边界的
26                     r++,sum+=a[r];
27 
28                 ans-=(r-i+1);//减去由L与i及其右边组成的
29                 sum-=a[L];
30             }
31         }
32         cout<<ans<<endl;
33     }
34 }
View Code

H.Magic Line(思维)

•题意

        给你 n 个点,满足 n 为偶数,并且这 n 个为不重复的整数点;

  求将这 n 个点均分为两部分的直线 L,即有 n/2 个点严格在 L 上方,n/2个点严格在 L 下方;

  输出 L 上的任意两个点 (x1,y1),(x2,y2) 的坐标;

  要 x1,y1,x2,y2 为整数,且 |x1|,|y1|,|x2|,|y2| ≤ 109;

•思路

看到这个题,我第一反应是分蛋糕的题

但是他不是!

然后...然后就game over了

用出题人的话来说就是

假设没有x重复,可以按x排序,然后在中间画一条线

现在有x重复,可以按照(x,y)双关键字排序,在中间偏左和中间偏右的两个中间点之间,画一条稍微稍微倾斜的线

两种实现方式的具体细节在代码中解释

•代码

实现方式一

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int inf=1e8+5;
 4 struct node
 5 {
 6     int x,y;
 7 }s[1005];
 8 
 9 bool cmp(node a,node b)
10 {
11     if(a.x==b.x)
12         return a.y<b.y;
13     return a.x<b.x;
14 }
15 
16 int main()
17 {
18     int t;
19     cin>>t;
20     while(t--)
21     {
22         int n;
23         cin>>n;
24         for(int i=1;i<=n;i++)
25             cin>>s[i].x>>s[i].y;
26         sort(s+1,s+1+n,cmp);
27         int x1=s[n/2].x-1;
28         int x2=s[n/2+1].x+1;
29         int y1=s[n/2].y+inf;
30         int y2=s[n/2+1].y-inf;
31         cout<<x1<<' '<<y1<<' '<<x2<<' '<<y2<<endl;
32         //①为什么要±1
33         //稍微倾斜一下,并且保证不能有交点
34         //x±(0,1],又因为是整数,x±1
35               
36         //②为什么是inf
37         //y变化的很大,x变化的很小,这条线及其接近于竖直
38         //才是稍微稍微倾斜一下
39         
40         //③为什么是±inf
41         //y±相同的数,使得中间两个点这关于条线对称
42         //正好分成左右两个部分使得左右的点个数相同
43     }
44 }
View Code

实现方式二

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 const int maxn=1e8+5;
 5 struct node
 6 {
 7     int x,y;
 8 }s[maxn];
 9 bool cmp(node a,node b)
10 {
11     if(a.x==b.x)
12         return a.y<b.y;
13     return a.x<b.x;
14 }
15 
16 int t;
17 int main()
18 {
19     cin>>t;
20     while(t--)
21     {
22         int n;
23         cin>>n;
24         for(int i=1;i<=n;i++)
25             cin>>s[i].x>>s[i].y;
26         sort(s+1,s+n+1,cmp);
27         int xx=s[n/2].x;
28         int yy=s[n/2].y;
29         cout<<xx-1<<' '<<yy+maxn<<' '<<xx+1<<' '<<yy-maxn+1<<endl;
30         //①为什么要±1
31         //稍微倾斜一下,并且保证不能有交点
32         //x±(0,1],又因为是整数,x±1
33         
34         //②为什么要是inf
35         //y变化的很大,x变化的很小,这条线及其接近于竖直
36         //才是稍微稍微倾斜一下
37         
38         //③为什么是±inf
39         //y±相同的数,使得中间两个点这关于条线对称
40         //正好分成左右两个部分使得左右的点个数相同
41         
42         //④为什么y要+1
43         //中间可能有整点,为了防止对称的时候过中间的整点y+1
44         //为什么是+1,因为x2-x1>=2,当x2-x1=2时,k=y2-y1+2/x2-x1=(y2-y1/x2-x1)+1
45         //可能过整点,就可能相交!
46     }
47 }
View Code

J.LRU management(模拟 unordered_map+list)

•题意

一个固定长度的LRU数组,每次有两种操作

0号操作:若查询的字符串在数组中存在,就把它取出来,并原有值保持不变,压入数组末尾。

      若查询的字符串不存在则直接加入到末尾,值为输入的v。

      输出此字符串的值v

1号操作:先得出输入的字符串在数组中的下标k,查询下标为(k+v)的数组元素的值,

      若k或(k+v)不存在都输出Invalid。

•思路

用unordered_map<string,list<>::iterator>来模拟

不会这两个函数的可以先去看下用法....

注意用cout时换行用endl会TLE,改成' '就会AC,时间快的还很多

以前都不知道endl这么费时间_(:з」∠)_

•代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 struct Node
 4 {
 5     string ss;
 6     int v;
 7 };
 8 list<Node> l;
 9 unordered_map<string,list<Node>::iterator> mp;
10 
11 int main()
12 {
13     std::ios::sync_with_stdio(false),cin.tie(0);
14     int t;;
15     cin>>t;
16     while(t--)
17     {
18         int n,m;
19         cin>>n>>m;
20         int op,v;
21         string s;
22         mp.clear(),l.clear();
23         for(int i=1;i<=n;i++)
24         {
25             cin>>op>>s>>v;
26 
27             ///1操作
28             if(op)
29             {
30                 ///存在则得出输入的字符串在数组中的下标k,查询下标为(k+v)的数组元素的值
31                 if(mp.count(s))
32                 {
33                     list<Node> ::iterator it=mp[s];
34                     if(v==-1)
35                     {
36                         if(it==l.begin())
37                             cout<<"Invalid
";
38                         else
39                         {
40                             it--;
41                             cout<<it->v<<"
";
42                         }
43                     }
44                     else if(v==0)
45                         cout<<it->v<<"
";
46                     else
47                     {
48                         it++;
49                         if(it==l.end())
50                             cout<<"Invalid"<<"
";
51                         else
52                             cout<<it->v<<"
";
53                     }
54                 }
55                 ///若k或(k+v)不存在都输出Invalid。
56                 else
57                     cout<<"Invalid
";
58             }
59             ///0操作
60             else
61             {
62                 ///存在则 把它取出来,原有值保持不变,压入数组末尾。
63                 if(mp.count(s))
64                 {
65                     list<Node> ::iterator it=mp[s];
66                     cout<<it->v<<"
";
67                     l.push_back(Node{it->ss,it->v});
68                     l.erase(it);
69                     it=l.end();
70                     it--;
71                     mp[s]=it;
72                 }
73                 ///不存在则直接加入到末尾,值为输入的v。
74                 else
75                 {
76                     cout<<v<<"
";
77                     l.push_back(Node{s,v});
78                     list<Node> ::iterator it=l.end();
79                     it--;
80                     mp[s]=it;
81                 }
82                 int sz=l.size();
83                 while(sz>m)
84                 {
85                     mp.erase(l.begin()->ss);
86                     l.pop_front();
87                     sz--;
88                 }
89             }
90         }
91     }
92 }
View Code
原文地址:https://www.cnblogs.com/MMMinoz/p/11251613.html