ICPC网络预选赛(第一场)(一)

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
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值