ICPC网络预选赛(第一场)(一)包含M,A,F,G
文章目录
题目链接
M Find the Easiest Problem
题目大意
选择最多队数(不同队)通过的题目最简单,如果人数相同,则按字典序进行选择,选择字典序小的。
目的就是找到最容易的问题,
解题思维
为了保证改题目只能因一个人通过多次时,记为1次 ,可以通过哈希表,判断是否某个队针对某个题已经通过。
解题代码
#include<bits/stdc++.h>
using namespace std;
void solve()
{
int n;
cin>>n;
string a,b,c;
//题目是从‘A’~‘Z’,记录该题目的通过数量
int m[255]={};
map<string,bool>mp;
while(n--)
{
cin>>a>>b>>c;
//如果某队对某题目没有通过,且满足通过的申请,则进行相应的处理
if(c.compare("accepted")==0&&!mp.count(a+b))
{
m[b[0]]++;
mp[a+b]=true;
}
}
int max_=0,index_=0;
//找到通过题目数量最多的第一个题目即可。
for(int i='A';i<='Z';i++)if(m[i]>max_) max_=m[i],index_=i;
cout<<(char)index_<<"\n";
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int t;
cin>>t;
while(t--)solve();
}
A World Cup
题目大意
根据比赛的规则,将32个人分为8个组,每组4人
分别为A,B,C,D,E,F,G,H
第一轮中每队实力前2的晋级16强
(N)表示该竞争中的获胜者
第二轮中(1)A1vsB2,(2)C1vsD2,(3)E1vsF2(4)G1VSH2(5)A2vsB1,(6)C2vsD1,(7)E2vsF1(8)G2vsH1 晋级8强
第三轮中(9) :(1)vs(2),10:(3)vs(4),11:(5)vs(6),12:(7)vs(8)晋级四强
第四轮前两个vs和后两个vs晋级二强
第五轮决出第一
中国队是32个中第一个输入的数据,尽量让中国队的排名最靠前
解题思维
显然这题是一题找规律的题,那就一个个推出来
由于只在乎实力的排序,可以将输入的32个值,根据实力的大小映射为1~32,1最弱,32最强
想要晋级16强,至少得>2(实力排名)
如果想要晋级8强,可以用A队和B队来模拟,为了 保证找到最小能晋级8强的排名,应该从前8个数据中找
1 2 3 4 ,5 6 7 8
前四个A队,后四个B队
晋级A1:4,A2:3 ,B1:8,B2:8
7晋级
那么尝试让6晋级,思维应该是想办法让A1变成6,让B2小于6,发现根本无法做到,由此晋级8强的条件就是>=7
现在考虑晋级4强,考虑前四个队,同样的思路,显然要在1~16中找到最小的,找到最小能满足晋级16强的排名
1 2 3 4 ,5 6 7 8 ,9 10 11 12 ,13 14 15 16
3 4,7 8,11 12,15 16
7 15
发现15可以晋级,尝试让14晋级,因为14对于之前的队是很大的,所以应该放在A1的位置,为了使得C1,D2小于14,应该尽量让D2小于14,发现A1是和B2进行比较,意味着B1可以放一个比14大的数,发现15满足,条件。放了之后显然只有16大于14,满足条件
1 2 3 14, 5 6 7 15, 9 10 11 12 ,4 8 13 16,比如满足条件
当变成13时,由于最多只能放一个比13大的数,而比13大的数有 两个(13,15)所以13就不可以了。因此晋级4强的条件是>=14
晋级二强就要考虑32个数字了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
(1)A1vsB2,(2)C1vsD2,(3)E1vsF2(4)G1VSH2(5)A2vsB1,(6)C2vsD1,(7)E2vsF1(8)G2vsH1
可以先只看一边(1)A1vsB2,(2)C1vsD2,(3)E1vsF2(4)G1VSH2,因为只通过这一边就可以晋级出一个二强
由于A1可以保证数字最大,B2,D2,F2,H2意味着可以存储比A1大的数字四个,也就说明32-4就是能晋级二强的条件也就是28,例如
1 2 3 28, 5 6 7 29, 9 10 11 12, 13 14 15 30, 17 18 19 20, 21 22 23 31,25 26 27 4, 8 16 24 32
28,3 29,7 11,12 15,30 19,20 23,31 26,27 24,32
28 15 23 27
28 27
28
由此推出的规律
32
:
a
<
3
16
:
a
>
2
&
&
a
<
7
8
:
a
>
6
&
&
a
<
14
4
:
a
>
13
&
&
a
<
28
2
:
a
>
27
&
&
a
<
32
1
:
a
=
32
由此推出的规律\\ 32:a<3\\ 16:a>2\&\&a<7\\ 8:a>6\&\&a<14\\ 4:a>13\&\&a<28\\ 2:a>27\&\&a<32\\ 1:a=32
由此推出的规律32:a<316:a>2&&a<78:a>6&&a<144:a>13&&a<282:a>27&&a<321:a=32
解题代码
#include<bits/stdc++.h>
using namespace std;
#define pil pair<int,int>
#define fi first
#define se second
void solve()
{
vector<pil> a(33);
for(int i=1;i<=32;i++)
{
cin>>a[i].fi;
a[i].se =i;
}
sort(a.begin()+1,a.end());
int b;
for(int i=1;i<=32;i++) if(a[i].se==1)
{
b = i;
break;
}
if(b<3) cout<<32<<"\n";
else if(b<7) cout<<16<<"\n";
else if(b<14) cout<<8<<"\n";
else if(b<28) cout<<4<<"\n";
else if(b<32) cout<<2<<"\n";
else if(b==32) cout<<1<<"\n";
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0) ,cout.tie(0);
int t;
cin>>t;
while(t--)solve();
}
F Make Max
题目大意
可以任意选择子数组(长度必须大于1),使得子数组所有值变为最大的,这样操作记为1次,要求求出最大的操作次数
解题思维
对于 3 2 1
最好的思路显然是 2影响1,3影响2个2
对于3 2 1 3 2 1
最好的思路和上面一样,
对于4 2 1 3 2 1
最好的思路显然应该是2影响周围1,3影响周围变成2的数,最后在到4
发现规律,一个数能够影响(左边第一个大于等于它或者边界,右边第一个大于等于它或者边界)因为有321321的例子,可以发现,两个3影响的地方明显有重复的地方(可以根据顺序,来判断是否这些数是否已经被其他相同大小的元素影响过了,后边看代码处理成绩处理方式)
做题思维是使用单调栈做的,这个栈是单调递减的。
依次从左右两边,找到第一个大于它的数位置
解题代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 999;
int a[N],l[N],r[N],st[N];
void solve()
{
int n;
cin>>n;
int top=0;
st[0] = 0;
for(int i=1;i<=n;i++)cin>>a[i];
//通过单调栈,找到第一个大于它的索引-1,表明了他能影响的范围
//能影响到左边界(栈空),的记为能影响到第一个元素
for(int i=1;i<=n;i++)
{
while(top&&a[st[top]]<a[i])
top--;
l[i] = st[top]+1;
st[++top]=i;
}
top = 0 ;
st[0] = n+1;
a[n + 1] = 0;
//右边也是相同的处理,不过是倒序
for(int i=n;i>0;i--)
{
while(top&&a[st[top]]<a[i])
top--;
r[i] = st[top]-1;
//由于可能存在重复元素影响的情况,当遇到这种情况时,只需要让该元素在该方向不产生影响即可(避免重复影响)
if(a[r[i] + 1] == a[i])
r[i] = i;
st[++top]=i;
}
int ans = 0;
for (int i = 1; i <= n; i++)
{
// cout<<a[i]<<" "<<"l,r:"<<l[i]<<","<<r[i]<<"\n";
ans += r[i] - l[i];
}
cout <<ans << '\n';
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
while(t--) solve();
}
G the Median of the Median of the Median
题目大意
找中位数的中位数的中位数,这个怎么理解呢,题目对于中位数的定义是指在一个区间范围内第[区间长度/2]小的元素,也就是分好区间后升序排序,选取第(区间长度/2)的元素
举个例子说明要干啥
比如实例1
4
1 3 1 7
那么就可以分割为10个区间也就是
[1,1] 1 它的中位数是1
[1,2] 1,3它的中位数是1
[1,3] 1,3,1它的中位数是1
[1,4] 1,3,1,7它的中位数是1
[2,2] 3它的中位数是3
[2,3] 3,1它的中位数是1
[2,4] 3,1,7它的中位数是3
[3,3] 1它的中位数是1
[3,4] 1,7 它的中位数是1
[4,4] 7它的中位数是7
也就是形成一个倒三角形状的图形,可以用一个二维数组表示,其中i表示了区间起点,j表示了区间终点
那c数组如何获取呢
他也是同样的道理像上面那样分割,不过它是基于上面的二维数组进行分割
假如要得到l=1,r=1构成的序列
假如要得到l=1,r=2构成的序列
假如要得到l=2,r=3构成的序列
依次类推,将所有情况的序列升序排序后,再拿出中位数放入c即可
最终得到的所有情况答案
最终答案就是拿升序后构成c的数的中位数
发现所有c的中位数就是1
解题思路
由于所有区间都会进行排序后,选择最中间的数当作中位数。那么对于 一个排好序的数 1 2 3 ~n(中位数可能是里面的数),应该符合一种大致规律,就是越靠近中位数,那么某种影响应该越小,而在中位数的两边的数,应该会产生不同的影响,在最逼近时,也就是l==r时,即为答案,根据这种性质采用二分的方法逼近答案。
上文影响:指的是对于原数组,比这个选定中位数小的记为-1,另外的情况记为1
解题代码
这是引用了大佬做的代码,只做解释哈。
#include <bits/stdc++.h>
using namespace std;
const int maxn=2005;
int n;
int A[maxn],a[maxn];
int s[maxn][maxn];
int c[maxn][maxn];
int get(int x1,int y1,int x2,int y2){
return s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1];
}
bool check(int k){//>=k的置为1,否则置为-1
//进行了前缀和的同时将选定中位数与原数组进行了比较,使得可以快速知道某个区间内的影响情况
for(int i=1;i<=n;i++)a[i]=a[i-1]+((A[i]>=k)?1:-1);
//初始化二维数组
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
s[i][j]=0;
//如果某个区间内的影响情况>0,记为1,否则记为-1
for(int l=1;l<=n;l++)
for(int r=l;r<=n;r++)
s[l][r]=(a[r]-a[l-1]>0)?1:-1;
//这里进行的是,一种得到从坐标0,0到坐标(i,j)矩阵范围内,包含数据总和的动态规划,使得能够快速得到某个子矩阵包含数据总和的大小。
for(int l=1;l<=n;l++)
for(int r=1;r<=n;r++)
{
s[l][r]=s[l][r]+s[l-1][r]+s[l][r-1]-s[l-1][r-1];
}
int ans=0;
for(int l=1;l<=n;l++)
for(int r=l;r<=n;r++){
//通过之前的动态规划,把c所有的情况的影响取出来。
ans+=get(l,l,r,r)>0?1:-1;
}
//返回该中位数产生的影响情况
return ans>0;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)cin>>A[i];
//a_i最大的也就是1e9,最后中位数的范围也就确定了
int l=1,r=1e9,mid;
//二分去逼近
while(l<r){
mid=(l+r+1)>>1;
if(check(mid))l=mid;
else r=mid-1;
}
cout<<l<<endl;
return 0;
}
/*
8
3 3 8 4 5 3 8 5
4
1 3 1 7
*/
r=l;r<=n;r++){
//通过之前的动态规划,把c所有的情况的影响取出来。
ans+=get(l,l,r,r)>0?1:-1;
}
//返回该中位数产生的影响情况
return ans>0;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)cin>>A[i];
//a_i最大的也就是1e9,最后中位数的范围也就确定了
int l=1,r=1e9,mid;
//二分去逼近
while(l<r){
mid=(l+r+1)>>1;
if(check(mid))l=mid;
else r=mid-1;
}
cout<<l<<endl;
return 0;
}
/*
8
3 3 8 4 5 3 8 5
4
1 3 1 7
*/